POI大量数据读取内存溢出分析及解决方案

点击关注公众号,实用技术文章及时了解d8add86981a8452177ffbc579121ee13.png

来源:blog.csdn.net/liangjf85/article/

details/84914798

在使用POI进行excel操作时,当数据量较大时经常会产生内存溢出异常。下面我们通过分析如何解决该问题

一、POI结构图

3d1efed6c5f1cbbd587601d73f22314b.png

二、内存溢出问题

在项目中遇到二十万行数据要写入到excel中时会内存溢出,一般方法是调大tomcat的内存,但是调到2048M还是会内存溢出报错。因此我们分析其原因。

我们通过分析其源码,得出其实现步骤为通过InputStream一行行读取到TreeMap类型的HSSFRow结构体中,因此当数据量大时就会造成内存溢出。

public HSSFWorkbook(DirectoryNode directory, boolean preserveNodes)
            throws IOException
    {
        super(directory);
        String workbookName = getWorkbookDirEntryName(directory);
 
        this.preserveNodes = preserveNodes;
 
        // If we're not preserving nodes, don't track the
        //  POIFS any more
        if(! preserveNodes) {
            clearDirectory();
        }
 
        _sheets = new ArrayList<HSSFSheet>(INITIAL_CAPACITY);
        names  = new ArrayList<HSSFName>(INITIAL_CAPACITY);
 
        // Grab the data from the workbook stream, however
        //  it happens to be spelled.
        InputStream stream = directory.createDocumentInputStream(workbookName);
 
        List<Record> records = RecordFactory.createRecords(stream);
 
        workbook = InternalWorkbook.createWorkbook(records);
        setPropertiesFromWorkbook(workbook);
        int recOffset = workbook.getNumRecords();
 
        // convert all LabelRecord records to LabelSSTRecord
        convertLabelRecords(records, recOffset);
        RecordStream rs = new RecordStream(records, recOffset);
        while (rs.hasNext()) {
            try {
                InternalSheet sheet = InternalSheet.createSheet(rs);
                _sheets.add(new HSSFSheet(this, sheet));
            } catch (UnsupportedBOFType eb) {
                // Hopefully there's a supported one after this!
                log.log(POILogger.WARN, "Unsupported BOF found of type " + eb.getType());
            }
        }
 
        for (int i = 0 ; i < workbook.getNumNames() ; ++i){
            NameRecord nameRecord = workbook.getNameRecord(i);
            HSSFName name = new HSSFName(this, nameRecord, workbook.getNameCommentRecord(nameRecord));
            names.add(name);
        }
    }
/**
     * add a row to the sheet
     *
     * @param addLow whether to add the row to the low level model - false if its already there
     */
 
    private void addRow(HSSFRow row, boolean addLow) {
        _rows.put(Integer.valueOf(row.getRowNum()), row);
        if (addLow) {
            _sheet.addRow(row.getRowRecord());
        }
        boolean firstRow = _rows.size() == 1;
        if (row.getRowNum() > getLastRowNum() || firstRow) {
            _lastrow = row.getRowNum();
        }
        if (row.getRowNum() < getFirstRowNum() || firstRow) {
            _firstrow = row.getRowNum();
        }
    }

excel数据行读取到内存的存储结构如下:

64224cb7afc24d416a22b633361e0ece.png

三、解决方案

poi官网给了一种大批量数据写入的方法,使用SXXFWorkbook类进行大批量写入操作解决了这个问题,可以监控该样例,我们会发现整体内存呈现锯齿状,能够及时回收,内存相对比较平稳。

package org.bird.poi;
 
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
 
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.junit.Assert;
 
public class XSSFWriter {
 
 private static SXSSFWorkbook wb;
 
 public static void main(String[] args) throws IOException {
        wb = new SXSSFWorkbook(10000);
        Sheet sh = wb.createSheet();  
        for(int rownum = 0; rownum < 100000; rownum++){  
            Row row = sh.createRow(rownum);  
            for(int cellnum = 0; cellnum < 10; cellnum++){  
                Cell cell = row.createCell(cellnum);  
                String address = new CellReference(cell).formatAsString();  
                cell.setCellValue(address);  
            }  
  
        }  
  
        // Rows with rownum < 900 are flushed and not accessible  
        for(int rownum = 0; rownum < 90000; rownum++){  
          Assert.assertNull(sh.getRow(rownum));  
        }  
  
        // ther last 100 rows are still in memory  
        for(int rownum = 90000; rownum < 100000; rownum++){  
            Assert.assertNotNull(sh.getRow(rownum));  
        }  
        URL url = XSSFWriter.class.getClassLoader().getResource("");
          
        FileOutputStream out = new FileOutputStream(url.getPath() + File.separator + "wirter.xlsx");  
        wb.write(out);  
        out.close();  
  
        // dispose of temporary files backing this workbook on disk  
        wb.dispose();  
 }
}

c1ab1bdd8ff7e8a9c114007a76eb84df.png

推荐

主流Java进阶技术(学习资料分享)

Java面试题宝典

加入Spring技术开发社区

7387d21ab279d3fb9845e2eab1f1e65f.png

PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
java导入excel大量数据出现内存溢出的问题可以通过以下几种方法来解决: 1. 分批导入:将大量数据分成多个小批次导入,每次只处理一部分数据,避免一次性加载全部数据导致内存溢出。可以按照行或者列进行分批导入,读取一部分数据后进行处理,然后再读取下一部分数据。 2. 内存优化:在导入数据时,可以优化内存使用,减少内存消耗。例如,使用SXSSF模式代替XSSF模式,SXSSF模式可以将Excel数据写入临时文件而不是全部加载到内存中,减少内存压力。 3. 限制每次读取数据量:可以通过设置读取数据的行数或者列数限制来减少内存使用。例如,使用Apache POI库的setReadWindow方法来限制每次读取的行数和列数。 4. 增加JVM内存:通过增加JVM的内存限制来避免内存溢出问题。可以通过调整-Xms和-Xmx参数来增加JVM的初始内存和最大内存限制。 5. 优化代码逻辑:检查和优化导入数据的代码逻辑,确保没有内存泄漏或者无用的数据加载。可以使用工具来进行代码分析,找出潜在的问题并进行优化。 6. 使用缓存机制:对于重复的数据,可以使用缓存机制来减少重复加载。可以将已经读取数据缓存起来,在需要的时候直接从缓存中获取,避免重复读取导致的内存占用过高。 7. 使用数据库存储:对于大量数据的导入,可以考虑将数据存储在数据库中,而不是全部加载到内存中。可以使用数据库的批量插入操作来提高导入效率。 综上所述,解决java导入excel大量数据出现内存溢出问题可以通过分批导入、内存优化、限制每次读取数据量、增加JVM内存、优化代码逻辑、使用缓存机制以及使用数据库存储等方法来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值