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

前言

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

分析

1.dump内存文件

liunx使用如下命令:

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

2.使用Eclipse Memory Analysis进行分析

异常如下:

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);
  }
 }
}

具体代码来源:http://poi.apache.org/spreadsheet/how-to.html#xssf_sax_api

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监控结果如下:

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

5.设置VM arguments:-Xms200m -Xmx200m

UserModel可以正常运行,使用Java VisualVM监控结果如下:

EventModel可以正常运行,使用Java VisualVM监控结果如下:

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

总结

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

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

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

(0)

相关推荐

  • Java使用poi操作excel实例解析

    本文实例为大家分享了Java使用poi操作excel的具体代码,供大家参考,具体内容如下 依赖poi的jar包,pom.xml配置如下: <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0h

  • Java 使用poi把数据库中数据导入Excel的解决方法

    Java 利用poi把数据库中数据导入Excel 效果: 使用时先把poi包导入工程的path,注意只需要导入poi包即可,下载后有三个jar包 核心代码: 连接数据库:DBConnection.java 复制代码 代码如下: package org.xg.db;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;i

  • java poi读取excel操作示例(2个代码)

    项目中要求读取excel文件内容,并将其转化为xml格式.常见读取excel文档一般使用POI和JExcelAPI这两个工具.这里我们介绍使用POI实现读取excel文档. 复制代码 代码如下: /* * 使用POI读取EXCEL文件 */import java.io.File;import java.io.FileInputStream;import java.util.ArrayList; import org.apache.poi.hssf.usermodel.HSSFCell;impor

  • java使用poi读取excel内容方法实例

    复制代码 代码如下: import java.io.BufferedInputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.nio.channels.FileChannel;import java.text.DecimalFormat;import java.text.SimpleDat

  • JAVA使用POI获取Excel的列数与行数

    前言 报表输出是Java应用开发中经常涉及的内容,而一般的报表往往缺乏通用性,不方便用户进行个性化编辑.Java程序由于其跨平台特性,不能直接操纵Excel.因此,本文探讨一下POI视线Java程序进行Excel中列数和行数的读取. 方法如下 //获取指定行,索引从0开始 hssfRow=hssfSheet.getRow(1); //获取指定列,索引从0开始 hssfCell=hssfRow.getCell((short)6); //获取总行数 //int rowNum=hssfSheet.ge

  • Java利用POI实现导入导出Excel表格示例代码

    介绍 Jakarta POI 是一套用于访问微软格式文档的Java API.Jakarta POI有很多组件组成,其中有用于操作Excel格式文件的HSSF和用于操作Word的HWPF,在各种组件中目前只有用于操作Excel的HSSF相对成熟.官方主页http://poi.apache.org/index.html,API文档http://poi.apache.org/apidocs/index.html 实现 已经在代码中加入了完整的注释. import java.io.FileInputSt

  • java的poi技术读取和导入Excel实例

    报表输出是Java应用开发中经常涉及的内容,而一般的报表往往缺乏通用性,不方便用户进行个性化编辑.Java程序由于其跨平台特性,不能直接操纵Excel.因此,本文探讨一下POI视线Java程序进行Excel的读取和导入. 项目结构: java_poi_excel 用到的Excel文件: xls XlsMain .java 类 //该类有main方法,主要负责运行程序,同时该类中也包含了用poi读取Excel(2003版) import java.io.FileInputStream; impor

  • java使用poi读取ppt文件和poi读取excel、word示例

    Apache的POI项目可以用来处理MS Office文档,codeplex上还有一个它的.net版本.POI项目可创建和维护操作各种基于OOXML和OLE2文件格式的Java API.大多数MS Office都是OLE2格式的.POI通HSMF子项目来支持Outlook,通过HDGF子项目来支持Visio,通过HPBF子项目来支持Publisher. 使用POI抽取Word简单示例: 要引入poi-3.7.jat和poi-scratchpad-3.7.ajr这两个包. 复制代码 代码如下: p

  • jsp利用POI生成Excel并在页面中导出的示例

    java中导出Excel有两个组件可以使用,一个是jxl,一个是POI,我这里用的是POI.导出是可以在服务器上生成文件,然后下载,也可以利用输出流直接在网页 中弹出对话框提示用户保存或下载.生成文件的方式会导致服务器中存在着垃圾文件,实现方式不太优雅,所以这里我采用的是后面直接通过输出流的方式. 1.修改WEB服务器的CONF/web.xml,添加 Xml代码 <mime-mapping> <extension>xls</extension> <mime-typ

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

    前言 最近生产环境有个老项目一直内存报警,不时的还出现内存泄漏,导致需要重启服务器,已经严重影响正常服务了. 分析 1.dump内存文件 liunx使用如下命令: ./jmap -dump:format=b,file=heap.hprof pid 2.使用Eclipse Memory Analysis进行分析 异常如下: at org.apache.poi.xssf.usermodel.XSSFRow.<init>(Lorg/openxmlformats/schemas/spreadsheet

  • 有关tomcat内存溢出的完美解决方法

    tomcat内存溢出设置JAVA_OPTS 答案1 设置Tomcat启动的初始内存 其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4.可以利用JVM提供的-Xmn -Xms -Xmx等选项可 进行设置 三.实例,以下给出1G内存环境下java jvm 的参数设置参考: JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=64M -XX:MaxNewSize=256m -XX:MaxPermSize=128m -D

  • Tomcat内存溢出分析及解决方法

    JVM管理两种类型的内存,堆和非堆.堆是给开发人员用的上面说的就是,是在JVM启动时创建:非堆是留给JVM自己用的,用来存放类的信息的.它和堆不同,运行期内GC不会释放空间. 一.内存溢出类型 1.java.lang.OutOfMemoryError: PermGen space JVM管理两种类型的内存,堆和非堆.堆是给开发人员用的上面说的就是,是在JVM启动时创建;非堆是留给JVM自己用的,用来存放类的信息的.它和堆不同,运行期内GC不会释放空间.如果web app用了大量的第三方jar或者

  • 关于PHP内存溢出问题的解决方法

    一.内存溢出解决方案在做数据统计分析时,经常会遇到大数组,可能会发生内存溢出,这里分享一下我的解决方案.还是用例子来说明这个问题,如下:假定日志中存放的记录数为500000条,那么解决方案如下: 复制代码 代码如下: ini_set('memory_limit','64M'); //重置php可以使用的内存大小为64M,一般在远程主机上是不能修改php.ini文件的,只能通过程序设置.注:在safe_mode(安全模式)下,ini_set失效set_time_limit(600);//设置超时限

  • phpExcel导出大量数据出现内存溢出错误的解决方法

    phpExcel将读取的单元格信息保存在内存中,我们可以通过 复制代码 代码如下: PHPExcel_Settings::setCacheStorageMethod() 来设置不同的缓存方式,已达到降低内存消耗的目的! 1.将单元格数据序列化后保存在内存中 复制代码 代码如下: PHPExcel_CachedObjectStorageFactory::cache_in_memory_serialized; 2.将单元格序列化后再进行Gzip压缩,然后保存在内存中 复制代码 代码如下: PHPEx

  • java利用POI读取excel文件的方法

    摘要:利用java读取excel文件,读取文件并获取文件中每一个sheet中的值. 一.需要提前导入的包: import java.io.File; import java.io.FileInputStream; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook

  • 完美解决java读取大文件内存溢出的问题

    1. 传统方式:在内存中读取文件内容 读取文件行的标准方式是在内存中读取,Guava 和Apache Commons IO都提供了如下所示快速读取文件行的方法: Files.readLines(new File(path), Charsets.UTF_8); FileUtils.readLines(new File(path)); 实际上是使用BufferedReader或者其子类LineNumberReader来读取的. 传统方式的问题: 是文件的所有行都被存放在内存中,当文件足够大时很快就会

  • spring boot如何使用POI读取Excel文件

    目录 spring boot 使用POI读取Excel文件 Excel文件目录 重要说明 读取Excel文件 获取sheet表格及读写单元格内容 合并单元格 SpringBoot解析Excel 以批量导入课程为例 spring boot 使用POI读取Excel文件 Excel文件目录 Excel模板文件存了resourse目录下,如下图: <dependency> <groupId>org.apache.poi</groupId> <artifactId>

  • Java利用poi读取Excel详解实现

    目录 前言 第一步导入依赖 第二步实现测试类+测试 实际应用 前言 用户可以直接读取本地文件,也可以通过上传文件的形式读取excel 注意:poi对于读取到空白行的时候,会默认的认为是最后一行,将不会再读取空白行下面的数据 第一步导入依赖 <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</vers

随机推荐