【什么是POI,为什么它会导致内存溢出?】

什么是POI,为什么它会导致内存溢出

  • 什么是POI
    • Excel并没看到的那么小
    • POI的溢出原理
  • 拓展知识
    • 几种Workbook格式

什么是POI

Apache POl,是一个非常流行的文档处理工具,通常大家会选择用它来处理Excel文件。但是在实际使用的时候经常会遇到内存溢出的情况,那么,为啥他会导致内存溢出呢?

Excel并没看到的那么小

我们通常见到的xlsx文件,其实是一个个压缩文件。它们把若千个XML格式的纯文本文件压缩在一起,Excel就是读取这些压缩文件的信息,最后展现出一个完全图形化的电子表格。

所以,如果我们把xlsx文件的后缀更改为.zip或 .rar,再进行解压缩,就能提取出构成Excel的核心源码文件。解压会发现解压后的文件中有3个文件夹和1个XML格式文件:

在这里插入图片描述
_rels 文件夹看里面数据像是一些基础的配置信息,比如 workbook 文件的位置等信息一般不会去动它。

docProps 文件夹下重要的文件是一个app.xml,这里面主要存放了 sheet 的信息,如果想添加或编辑 sheet 需要改这个文件,其他文件都是一些基础信息的数据,比如文件所有者,创建时间等。

x文件夹是最重要的一个文件夹里面存放了Sheet 中的数据,行和列的格式,单元格的格式,sheet的配置信息等等信息。

所以,实际上我们处理的xlsx文件实际上是一个经过高度压缩的文件格式,背后是有好多文件支持的。所以,我们看到的一个文件可能只有2M,但是实际上这个文件未压缩情况下可能要比这大得多。

在这里插入图片描述
也就是说,POI在处理的时候,处理的实际上并不只是我们看到的文件大小,实际上比它的大小要大好几倍。

这是为什么明明我们处理的文件只有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) {Thread.sleep(100); //添加注释:暂停程序执行100毫秒// 根据不同数据类型处理数据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* *aparam 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 == nul1) {(pack.getParts();}}catch (InvalidFormatException  RuntimeException e) {IOUtils.close0uietly(pack);throw e;}
return pack;
}

这行代码的注释中说了:这个方法会把整个压缩文件都加载到内存中。也就是把整个 Excel 文档加载到内存中,可想而知,这在处理大型文件时是肯定会导致导致内存溢出的。

也就是说我们使用的XSSFWorkbook (包括HSSFWorkbook也同理) 在外理Excel的过程中会将整个Excel都加载到内存中,在文件比较大的时候就会导致内存溢出。

拓展知识

几种Workbook格式

POI中提供了很多种Workbook API来操作Excel,有的适合大文件读写,有的不适合。

SSFWorkbook

  • 用于处理Excel的.xsl格式(即Excel 97-2003)。

XSSFWorkbook

  • 用于处理 Excel 的.xlsx 格式(即 Excel 2007 及以后版本的)支持更大的数据集和更多的功能,如更好的样式和公式支持。但是相对于HSSFWorkbook,它在处理大数据集时可能占用更多内存。

SXSSFWorkbook

  • 用于处理xlsx 格式。它是 XSSFWorkbook 的流式版本,专门设计用于处理大数据集。通过将数据写入临时文件而非全部保留在内存中,显著减少内存消耗。特别适合用于创建大型数据集的 Excel 文件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/220096.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

textarea 网页文本框在光标处添加内容

在前端研发中我们经常需要使用脚本在文本框中插入内容。如果产品要求不能直接插入开始或者尾部,而是要插入到光标位置,此时我们就需要获取光标/光标选中的位置。 很多时候,我在格式化文本处需要选择选项,将选择的信息输入到光标位…

html 中vue3 的setup里调用element plus的弹窗 提示

引入Elementplus之后&#xff0c;在setup&#xff08;&#xff09;方法外面导入ElMessageBox const {ElMessageBox} ElementPlus 源码 &#xff1a; <!DOCTYPE html> <html> <head><meta charset"UTF-8"><!-- import Vue before Elemen…

CAN 三: STM32 CAN相关寄存器介绍

1、寄存器列表&#xff08;F1/F4/F7&#xff09; 寄存器名称作用CAN_MCRCAN主控制寄存器主要负责CAN工作模式的配置CAN_BTR位时序寄存器用来设置分频/TBS1/TBS2/TSWJ等参数&#xff0c;设置测试模式CAN_(T/R)IxR标识符寄存器存放(待发送/接收)的报文ID、扩展ID、IDE位及RTR位C…

SpringBoot之实体参数的详细解析

1.3 实体参数 在使用简单参数做为数据传递方式时&#xff0c;前端传递了多少个请求参数&#xff0c;后端controller方法中的形参就要书写多少个。如果请求参数比较多&#xff0c;通过上述的方式一个参数一个参数的接收&#xff0c;会比较繁琐。 此时&#xff0c;我们可以考虑…

XSS防御:内容安全策略 CSP工作原理、配置技巧与最佳实践

前言 公司部门安全合规改造计划&#xff0c;要求所有的Web站点统一添加CSP规则。对于CSP机制我只是之前在应付面试的时候背过相关的概念&#xff0c;并没有真正在项目中实践过。所以希望借助本次改造任务好好理解并实践CSP机制。 什么是CSP CSP的全称是 Content Security Po…

CCF 202104-2:邻域均值--C++

#include<iostream> #include<bits/stdc.h>using namespace std;int A[601][601]; int n;//长宽都为n个像素double FindNeighborSum(int i,int j,int r,int A[][601]) {int sum0;//像素和 int gs0;//领域 中的像素个数 for(int xi-r;x<ir;x)//找到每一个领域像素…

数据分析基础之《numpy(3)—基本操作》

一、基本操作 1、adarray.方法() 2、np.函数名() 二、生成数组的方法 1、生成0和1的数组 为什么需要生成0和1的数组&#xff1f; 我们需要占用位置&#xff0c;或者生成一个空的数组 &#xff08;1&#xff09;ones(shape[, dtype, order]) 生成一组1 shape&#xff1a;形…

NOIP2017提高组day2 - T2:宝藏

题目链接 [NOIP2017 提高组] 宝藏 题目描述 参与考古挖掘的小明得到了一份藏宝图&#xff0c;藏宝图上标出了 n n n 个深埋在地下的宝藏屋&#xff0c; 也给出了这 n n n 个宝藏屋之间可供开发的 m m m 条道路和它们的长度。 小明决心亲自前往挖掘所有宝藏屋中的宝藏。但…

【沐风老师】科研绘图3DMAX病毒建模教程

3dMax在科研绘图方面也有广泛的应用&#xff0c;本教程就给大家讲解病毒的建模方法&#xff0c;下面直接进入教程&#xff1a; 3dMax病毒建模方法&#xff1a; 1.启动3dMax&#xff0c;在视口中创建一个“几何球体”。方法&#xff1a;右边命令面板->创建->几何体->…

MySQL中EXPLAIN执行计划的分析

一. 执行计划能告诉我们什么&#xff1f; SQL如何使用索引联接查询的执行顺序查询扫描的数据函数 二. 执行计划中的内容 SQL执行计划的输出可能为多行&#xff0c;每一行代表对一个数据库对象的操作 1. ID列 ID列中的如果数据为一组数字&#xff0c;表示执行SELECT语句的顺…

当当狸AR智能学习图集跨越千年文明传承,邀您“面对面”与虚拟诗人互动对诗

中华传统文化底蕴深厚&#xff0c;余韵悠长。即使经过千年的历史裂变&#xff0c;依然历久铭心慰藉着一代又一代人的灵魂。千百年后的今天&#xff0c;成为了我们独一无二的财富。 如今&#xff0c;国人学习中华传统文化的方式有很多&#xff0c;诗词集、动画影片、诗歌传颂等…

Java,这是一个模仿HashMap的put,get功能的自定义MyHashMap

Java 手写HashMap源码 一&#xff0c;手写源码 这是一个模仿HashMap的put&#xff0c;get功能的自定义的MyHashMap package cn.wxs.demo;import java.io.Serializable; import java.util.*; import java.util.function.BiConsumer; import java.util.function.BiFunction; i…

SIM初始化流程

ATR ATR(Answer To Reset)&#xff1a;复位应答信号&#xff0c;有SIM卡传输给终端&#xff0c;包括SIM卡自身的一些信息&#xff0c;比如支持的传输速率&#xff0c;传输模式等。 SIM卡的ATR代表"Answer to Reset"&#xff0c;即复位响应。当SIM卡被插入设备中时…

Linux驱动入门 —— 利用引脚号操作GPIO进行LED点灯

目录 一、字符设备驱动程序框架 编写驱动程序的步骤&#xff1a; 对于 LED 驱动&#xff0c;我们想要什么样的接口&#xff1f; LED 驱动能支持多个板子的基础&#xff1a;分层思想 二、Linux驱动如何指向一个GPIO 直接通过寄存器来操作GPIO 利用引脚号操作GPIO IMX6UL…

算法通关村第十八关-黄金挑战回溯困难问题

大家好我是苏麟 , 今天带来几道回溯比较困难的题 . 回溯有很多比较难的问题&#xff0c;这里我们看两个&#xff0c;整体来说这两个只是处理略复杂&#xff0c;还不是最难的问题 . 大纲 IP问题 IP问题 描述 : 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 …

redis:一、面试题常见分类+缓存穿透的定义、解决方案、布隆过滤器的原理和误判现象、面试回答模板

redis面试题常见分类 缓存穿透 定义 缓存穿透是一种现象&#xff0c;引发这种现象的原因大概率是遭到了恶意攻击。具体就是查询一个一定不存在的数据&#xff0c;mysql查询不到数据也不会直接写入缓存&#xff0c;就会导致这个数据的每次请求都需要查DB&#xff0c;数据库压力…

# 和 $ 的区别①

# 和 $ 都是为了获取变量的值 # 和 $ 区别 : 使用 # 查询 id 为 1 的内容 如果看不懂代码,就去看<<Mybatis 的操作(结合上文)续集>>,我这里为了简练一点就不多解释了 Select("select * from userInfo where id #{id}")UserInfo selectOne(Integer id…

Hive命令操作

1.命令行模式 1. 获取帮助 --> hive -H 或-help 2. 运行hive语句 --> hive -e "执行语句" 3. 运行hive文件 --> hive –f "执行文件" 4. 定义变量 --> hive –hivevar keyvalue 5. 引用变量 --> ${varname} 2. 交互模式 1. 进入客户端 -…

【UE 材质】切换颜色、纹理时的过渡效果

效果 步骤 1. 新建一个工程&#xff0c;创建Basic关卡 2. 创建一个材质&#xff0c;这里命名为“M_Plane”&#xff0c;打开这个材质&#xff0c;在材质图表中添加如下节点 注意“Noise”节点中的函数选择“Voronoi” 3. 对材质“M_Plane”创建材质实例 4. 在场景中放置一个平…

【SpringBoot】FreeMarker视图渲染

目录 一、FreeMarker 简介 1.1 什么是FreeMarker&#xff1f; 1.2 Freemarker模板组成部分 1.3 为什么要使用FreeMarker 二、Springboot集成FreeMarker 2.1 配置 2.2 数据类型 2.2.1 字符串 2.2.2 数值 2.2.3 布尔值 2.2.4 日期 2.3 常见指令 2.3.2 assign 2.3…