欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

关于Poi读取Excel引发内存溢出问题的解决方法

程序员文章站 2024-02-26 13:15:22
前言 最近生产环境有个老项目一直内存报警,不时的还出现内存泄漏,导致需要重启服务器,已经严重影响正常服务了。 分析 1.dump内存文件 liunx使用...

前言

最近生产环境有个老项目一直内存报警,不时的还出现内存泄漏,导致需要重启服务器,已经严重影响正常服务了。

分析

1.dump内存文件

liunx使用如下命令:

./jmap -dump:format=b,file=heap.hprof pid

2.使用eclipse memory analysis进行分析

关于Poi读取Excel引发内存溢出问题的解决方法

异常如下:

at org.apache.poi.xssf.usermodel.xssfrow.<init>(lorg/openxmlformats/schemas/spreadsheetml/x2006/main/ctrow;lorg/apache/poi/xssf/usermodel/xssfsheet;)v (xssfrow.java:68)
at org.apache.poi.xssf.usermodel.xssfsheet.initrows(lorg/openxmlformats/schemas/spreadsheetml/x2006/main/ctworksheet;)v (xssfsheet.java:157)
at org.apache.poi.xssf.usermodel.xssfsheet.read(ljava/io/inputstream;)v (xssfsheet.java:132)
at org.apache.poi.xssf.usermodel.xssfsheet.ondocumentread()v (xssfsheet.java:119)
at org.apache.poi.xssf.usermodel.xssfworkbook.ondocumentread()v (xssfworkbook.java:222)
at org.apache.poi.poixmldocument.load(lorg/apache/poi/poixmlfactory;)v (poixmldocument.java:200)
at org.apache.poi.xssf.usermodel.xssfworkbook.<init>(ljava/io/inputstream;)v (xssfworkbook.java:179)

poi在加载excel引发了内存泄漏,中间创建了大量的对象,占用了大量的内存

3.查看上传的excel大小

经查看发现很多excel大小在9m的文件

4.查看代码poi读取excel的方式

发现使用的是用户模式,这样会占用大量的内存;poi提供了2中读取excel的模式,分别是:

  • 用户模式:也就是poi下的usermodel有关包,它对用户友好,有统一的接口在ss包下,但是它是把整个文件读取到内存中的,
    对于大量数据很容易内存溢出,所以只能用来处理相对较小量的数据;
  • 事件模式:在poi下的eventusermodel包下,相对来说实现比较复杂,但是它处理速度快,占用内存少,可以用来处理海量的excel数据。

经上面分析基本可以确定问题出在使用poi的用户模式去读取excel大文件,导致内存泄漏。

本地重现

下面模拟一个600kb大小的excel(test.xlsx),分别用两种模式读取,然后观察内存波动;

1.需要引入的库maven:

<dependencies>
 <dependency>
  <groupid>org.apache.poi</groupid>
  <artifactid>poi-ooxml</artifactid>
  <version>3.6</version>
 </dependency>
 <dependency>
  <groupid>com.syncthemall</groupid>
  <artifactid>boilerpipe</artifactid>
  <version>1.2.1</version>
 </dependency>
</dependencies>

2.用户模式代码如下:

import java.io.file;
import java.io.fileinputstream;
import java.io.ioexception;
import java.io.inputstream;
 
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.usermodel.workbook;
import org.apache.poi.xssf.usermodel.xssfworkbook;
 
public class usermodel {
 
 public static void main(string[] args) throws interruptedexception {
  try {
   thread.sleep(5000);
   system.out.println("start read");
   for (int i = 0; i < 100; i++) {
    try {
     workbook wb = null;
     file file = new file("d:/test.xlsx");
     inputstream fis = new fileinputstream(file);
     wb = new xssfworkbook(fis);
     sheet sheet = wb.getsheetat(0);
     for (row row : sheet) {
      for (cell cell : row) {
       system.out.println("row:" + row.getrownum() + ",cell:" + cell.tostring());
      }
     }
    } catch (ioexception e) {
     e.printstacktrace();
    }
   }
   thread.sleep(1000);
  } catch (exception e) {
   e.printstacktrace();
  }
 }
}

3.事件模式代码如下:

import java.io.inputstream;
 
import org.apache.poi.openxml4j.opc.opcpackage;
import org.apache.poi.xssf.eventusermodel.xssfreader;
import org.apache.poi.xssf.model.sharedstringstable;
import org.apache.poi.xssf.usermodel.xssfrichtextstring;
import org.xml.sax.attributes;
import org.xml.sax.contenthandler;
import org.xml.sax.inputsource;
import org.xml.sax.saxexception;
import org.xml.sax.xmlreader;
import org.xml.sax.helpers.defaulthandler;
import org.xml.sax.helpers.xmlreaderfactory;
 
public class eventmodel {
 
 public void processonesheet(string filename) throws exception {
  opcpackage pkg = opcpackage.open(filename);
  xssfreader r = new xssfreader(pkg);
  sharedstringstable sst = r.getsharedstringstable();
 
  xmlreader parser = fetchsheetparser(sst);
  inputstream sheet2 = r.getsheet("rid1");
  inputsource sheetsource = new inputsource(sheet2);
  parser.parse(sheetsource);
  sheet2.close();
 }
 
 public xmlreader fetchsheetparser(sharedstringstable sst) throws saxexception {
  xmlreader parser = xmlreaderfactory.createxmlreader("org.apache.xerces.parsers.saxparser");
  contenthandler handler = new sheethandler(sst);
  parser.setcontenthandler(handler);
  return parser;
 }
 
 private static class sheethandler extends defaulthandler {
  private sharedstringstable sst;
  private string lastcontents;
  private boolean nextisstring;
 
  private sheethandler(sharedstringstable sst) {
   this.sst = sst;
  }
 
  public void startelement(string uri, string localname, string name, attributes attributes) throws saxexception {
   if (name.equals("c")) {
    system.out.print(attributes.getvalue("r") + " - ");
    string celltype = attributes.getvalue("t");
    if (celltype != null && celltype.equals("s")) {
     nextisstring = true;
    } else {
     nextisstring = false;
    }
   }
   lastcontents = "";
  }
 
  public void endelement(string uri, string localname, string name) throws saxexception {
   if (nextisstring) {
    int idx = integer.parseint(lastcontents);
    lastcontents = new xssfrichtextstring(sst.getentryat(idx)).tostring();
    nextisstring = false;
   }
 
   if (name.equals("v")) {
    system.out.println(lastcontents);
   }
  }
 
  public void characters(char[] ch, int start, int length) throws saxexception {
   lastcontents += new string(ch, start, length);
  }
 }
 
 public static void main(string[] args) throws exception {
  thread.sleep(5000);
  system.out.println("start read");
  for (int i = 0; i < 100; i++) {
   eventmodel example = new eventmodel();
   example.processonesheet("d:/test.xlsx");
   thread.sleep(1000);
  }
 }
}

具体代码来源:

4.设置vm arguments:-xms100m -xmx100m

usermodel运行结果直接报outofmemoryerror,如下所示:

exception in thread "main" java.lang.outofmemoryerror: gc overhead limit exceeded
 at java.lang.string.substring(string.java:1877)
 at org.apache.poi.ss.util.cellreference.separaterefparts(cellreference.java:353)
 at org.apache.poi.ss.util.cellreference.<init>(cellreference.java:87)
 at org.apache.poi.xssf.usermodel.xssfcell.<init>(xssfcell.java:105)
 at org.apache.poi.xssf.usermodel.xssfrow.<init>(xssfrow.java:68)
 at org.apache.poi.xssf.usermodel.xssfsheet.initrows(xssfsheet.java:157)
 at org.apache.poi.xssf.usermodel.xssfsheet.read(xssfsheet.java:132)
 at org.apache.poi.xssf.usermodel.xssfsheet.ondocumentread(xssfsheet.java:119)
 at org.apache.poi.xssf.usermodel.xssfworkbook.ondocumentread(xssfworkbook.java:222)
 at org.apache.poi.poixmldocument.load(poixmldocument.java:200)
 at org.apache.poi.xssf.usermodel.xssfworkbook.<init>(xssfworkbook.java:179)
 at zh.exceltest.usermodel.main(usermodel.java:23)

eventmodel可以正常运行,使用java visualvm监控结果如下:

关于Poi读取Excel引发内存溢出问题的解决方法

usermodel模式下读取600kbexcel文件直接内存溢出,看了600kbexcel文件映射到内存中还是占用了不少内存;eventmodel模式下可以流畅的运行。

5.设置vm arguments:-xms200m -xmx200m

usermodel可以正常运行,使用java visualvm监控结果如下:

关于Poi读取Excel引发内存溢出问题的解决方法

eventmodel可以正常运行,使用java visualvm监控结果如下:

关于Poi读取Excel引发内存溢出问题的解决方法

usermodel模式和eventmodel模式都可以正常运行,但是很明显usermodel模式回收内存更加频繁,而且在cpu的占用上更高。

总结

通过简单的分析以及本地运行两种模式进行比较,可以看到usermodel模式下使用的简单的代码实现了读取,但是在读取大文件时cpu和内存都不理想;

而eventmodel模式虽然代码写起来比较繁琐,但是在读取大文件时cpu和内存更加占优。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。