成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

快手二面:敢不敢說說為啥POI會導致內存溢出?

開發 開發工具
在POI中,提供了SXSSFWorkbook,通過將部分數據寫入磁盤上的臨時文件來減少內存占用。但是SXSSFWorkbook只能用于文件寫入,但是文件讀取還是不行的,就像我們前面分析過的,Excel的文件讀取還是會存在內存溢出的問題的。?

Apache POI,是一個非常流行的文檔處理工具,通常大家會選擇用它來處理Excel文件。但是在實際使用的時候,經常會遇到內存溢出的情況,那么,為啥他會導致內存溢出呢?

Excel并沒看到的那么小

我們通常見到的xlsx文件,其實是一個個壓縮文件。它們把若干個XML格式的純文本文件壓縮在一起,Excel就是讀取這些壓縮文件的信息,最后展現出一個完全圖形化的電子表格。

所以,如果我們把xlsx文件的后綴更改為.zip或.rar,再進行解壓縮,就能提取出構成Excel的核心源碼文件。解壓后會發現解壓后的文件中有3個文件夾和1個XML格式文件:

圖片圖片

_rels 文件夾 看里面數據像是一些基礎的配置信息,比如 workbook 文件的位置等信息,一般不會去動它. 

docProps 文件夾下重要的文件是一個 app.xml,這里面主要存放了 sheet 的信息,如果想添加或編輯 sheet 需要改這個文件.其他文件都是一些基礎信息的數據,比如文件所有者,創建時間等.

 xl 文件夾是最重要的一個文件夾,里面存放了 Sheet 中的數據,行和列的格式,單元格的格式,sheet 的配置信息等等信息.

所以,實際上我們處理的xlsx文件實際上是一個經過高度壓縮的文件格式,背后是有好多文件支持的。所以,我們看到的一個文件可能只有2M,但是實際上這個文件未壓縮情況下可能要比這大得多。

圖片圖片

也就是說,POI在處理的時候,處理的實際上并不只是我們看到的文件大小,實際上他的大小大好幾倍。(本文節選自我的《java面試寶典》)

這是為什么明明我們處理的文件只有100多兆,但是實際卻可能占用1G內存的其中一個原因。當然這只是其中一個原因,還有一個原因,我們就需要深入到POI的源碼中來看了。

POI溢出原理

我們拿POI的文件讀取來舉例,一般來說文件讀取出現內存溢出的情況更多一些。以下是一個POI文件導出的代碼示例:

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;


import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;


public class ExcelReadTest {


    public static void main(String[] args) {
        // 指定要讀取的文件路徑
        String filename = "example.xlsx";


        try (FileInputStream fileInputStream = new FileInputStream(new File(filename))) {
            // 創建工作簿對象
            Workbook workbook = new XSSFWorkbook(fileInputStream);


            // 獲取第一個工作表
            Sheet sheet = workbook.getSheetAt(0);


            // 遍歷所有行
            for (Row row : sheet) {
                // 遍歷所有單元格
                for (Cell cell : row) {                   
                    // 根據不同數據類型處理數據
                    switch (cell.getCellType()) {
                        case STRING:
                            System.out.print(cell.getStringCellValue() + "\t");
                            break;
                        case NUMERIC:
                            if (DateUtil.isCellDateFormatted(cell)) {
                                System.out.print(cell.getDateCellValue() + "\t");
                            } else {
                                System.out.print(cell.getNumericCellValue() + "\t");
                            }
                            break;
                        case BOOLEAN:
                            System.out.print(cell.getBooleanCellValue() + "\t");
                            break;
                        case FORMULA:
                            System.out.print(cell.getCellFormula() + "\t");
                            break;
                        default:
                            System.out.print(" ");
                    }
                }
                System.out.println();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }


}

這里面用到了一個關鍵的XSSFWorkbook類:

public XSSFWorkbook(InputStream is) throws IOException {
    this(PackageHelper.open(is));
}


public static OPCPackage open(InputStream is) throws IOException {
    try {
        return OPCPackage.open(is);
    } catch (InvalidFormatException e){
        throw new POIXMLException(e);
    }
}

最終會調用到OPCPackage.open方法,看看這個方法是咋實現的:

/**
 * Open a package.
 *
 * Note - uses quite a bit more memory than {@link #open(String)}, which
 * doesn't need to hold the whole zip file in memory, and can take advantage
 * of native methods
 *
 * @param in
 *            The InputStream to read the package from
 * @return A PackageBase object
 *
 * @throws InvalidFormatException
 *         Throws if the specified file exist and is not valid.
 * @throws IOException If reading the stream fails
 */
public static OPCPackage open(InputStream in) throws InvalidFormatException,
        IOException {
    OPCPackage pack = new ZipPackage(in, PackageAccess.READ_WRITE);
    try {
        if (pack.partList == null) {
            pack.getParts();
        }
    } catch (InvalidFormatException | RuntimeException e) {
        IOUtils.closeQuietly(pack);
        throw e;
    }
    return pack;
}

這行代碼的注釋中說了:這個方法會把整個壓縮文件都加載到內存中。也就是把整個 Excel 文檔加載到內存中,可想而知,這在處理大型文件時是肯定會導致導致內存溢出的。(本文節選自我的《java面試寶典》,里面有800多道面試常考題目)

也就是說我們使用的XSSFWorkbook(包括HSSFWorkbook也同理)在處理Excel的過程中會將整個Excel都加載到內存中,在文件比較大的時候就會導致內存溢出。

如何解決溢出問題?

在POI中,提供了SXSSFWorkbook,通過將部分數據寫入磁盤上的臨時文件來減少內存占用。但是SXSSFWorkbook只能用于文件寫入,但是文件讀取還是不行的,就像我們前面分析過的,Excel的文件讀取還是會存在內存溢出的問題的。

那如果要解決這個問題,可以考慮使用EasyExcel!(本文節選自我的《java面試寶典》,里面有800多道面試常考題目)

關于使用XSSFWorkbook和EasyExcel的文件讀取,我這里也做了個內存占用的對比,讀取一個27.3?MB的文件:

package excel.read;


import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;


import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;


public class XSSFExcelReadTest {


    public static void main(String[] args) {
        // 指定要讀取的文件路徑
        String filename = "example.xlsx";


        try (FileInputStream fileInputStream = new FileInputStream(new File(filename))) {
            // 創建工作簿對象
            Workbook workbook = new XSSFWorkbook(fileInputStream);


            // 獲取第一個工作表
            Sheet sheet = workbook.getSheetAt(0);


            // 遍歷所有行
            for (Row row : sheet) {
                // 遍歷所有單元格
                for (Cell cell : row) {
                    // 根據不同數據類型處理數據
                    switch (cell.getCellType()) {
                        case STRING:
                            System.out.print(cell.getStringCellValue() + "\t");
                            break;
                        case NUMERIC:
                            if (DateUtil.isCellDateFormatted(cell)) {
                                System.out.print(cell.getDateCellValue() + "\t");
                            } else {
                                System.out.print(cell.getNumericCellValue() + "\t");
                            }
                            break;
                        case BOOLEAN:
                            System.out.print(cell.getBooleanCellValue() + "\t");
                            break;
                        case FORMULA:
                            System.out.print(cell.getCellFormula() + "\t");
                            break;
                        default:
                            System.out.print(" ");
                    }
                }
                System.out.println();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }


}

使用Arthas查看內存占用情況:

圖片圖片

占用內存在1000+M。

改成使用EasyExcel同樣讀取同一份文件:

package excel.read;


import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;


public class EasyExcelReadTest {


    public static void main(String[] args) {
        // 指定要讀取的文件路徑
        String filename = "example.xlsx";


        EasyExcel.read(filename, new PrintDataListener()).sheet().doRead();
    }


}


// 監聽器,用于處理讀取到的數據
class PrintDataListener implements ReadListener<Object> {
    @Override
    public void invoke(Object data, AnalysisContext context) {
        // 處理每一行的數據
        System.out.println(data);
    }


    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 所有數據解析完成后的操作
    }


    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        // 處理讀取過程中的異常
    }
}

同樣使用Arthas查看內存占用情況:

圖片圖片

內存占用只有不到100M。

責任編輯:武曉燕 來源: Hollis
相關推薦

2014-01-23 16:53:49

2018-10-08 10:18:13

2015-02-09 17:38:56

愛情保鮮期平安WiFi

2009-05-04 09:26:39

2010-11-10 12:38:50

10G網絡以太網

2021-02-28 20:52:41

5G自動駕駛數據

2022-10-28 12:18:18

AI繪畫自拍

2013-03-28 13:33:39

魅族MEIZU招聘

2020-12-28 06:20:04

微信支付寶移動應用

2020-12-27 10:44:55

微信支付寶互聯網應用

2022-10-18 08:38:16

內存泄漏線程

2024-10-28 11:07:33

磁盤目錄文件

2020-01-17 20:00:25

SQL函數數據庫

2019-10-10 09:34:19

Python網絡爬蟲GitHub

2024-05-24 10:15:36

2024-10-24 16:51:08

2021-08-26 05:00:44

生產環境內存

2024-03-25 12:38:00

MySQL內存參數

2024-07-04 17:22:23

2024-03-11 08:22:40

Java內存泄漏
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成人h动漫亚洲一区二区 | 水蜜桃久久夜色精品一区 | 欧美成人精品在线 | 免费视频一区二区 | 精品国产乱码久久久久久图片 | 成人精品在线观看 | 欧美中文字幕一区二区三区 | 久热国产在线 | 国产99在线 | 欧美 | 国产精品麻 | 欧美日韩一区在线观看 | 一区二区三区中文字幕 | 免费观看一级毛片视频 | 性欧美精品一区二区三区在线播放 | 国产精品99久久久久久动医院 | 黄色一级大片在线免费看产 | 国产日韩精品一区二区三区 | 欧美色性 | 国产亚洲欧美日韩精品一区二区三区 | 欧美综合在线视频 | 色精品| 日韩欧美国产精品综合嫩v 一区中文字幕 | 午夜av成人 | 午夜小视频免费观看 | www.成人.com| 992tv人人草| 亚洲久草 | 中文一区二区视频 | a级片www| 国产精品亚洲欧美日韩一区在线 | 国产男女精品 | 成人免费日韩 | 天天久久| 人人做人人澡人人爽欧美 | 日韩在线国产 | 国产成人99久久亚洲综合精品 | 日韩精品免费一区二区在线观看 | 麻豆一区二区三区 | 国产在线观 | 亚洲一区三区在线观看 | 成人美女免费网站视频 |