解决问题:当你后端生成excel,前端调用接口,下载大文件之后,系统是否内存飙升,而且不像想象中会自动释放内存?用visualvm或者jconsole查看发现堆内存上升后,不会自动下降,必须手动gc之后才会?
本文来详细讲解如何处理该问题。
先上代码:
Workbook workbook = new SXSSFWorkbook();
try {
workbook.write(byteArrayOutputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
// 清除工作簿资源
try {
workbook.close();
((SXSSFWorkbook) workbook).dispose(); // 清理临时文件
System.gc(); // 手动调用垃圾回收器,ti
} catch (IOException e) {
throw new RuntimeException(e);
}
}
。。。
try {
if(byteArrayOutputStream!=null){
byteArrayOutputStream.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
下面详细来讲解:
序:注意!!!
在 Apache POI 库中,XSSFWorkbook
和 SXSSFWorkbook
类都有 close()
方法,但 dispose()
方法是 SXSSFWorkbook
特有的,用于清理临时文件。如果你在使用 XSSFWorkbook
时找不到 dispose()
方法,那是因为 XSSFWorkbook
类本身并没有这个方法。
以下是正确的使用方法:
- 对于
XSSFWorkbook
:
- 使用
close()
方法来关闭工作簿并释放资源。
try (FileOutputStream out = new FileOutputStream("output.xlsx")) {
workbook.write(out);
} finally {
workbook.close();
}
- 对于
SXSSFWorkbook
:
- 使用
close()
方法来关闭工作簿并释放资源。 - 使用
dispose()
方法来清理临时文件。
try (FileOutputStream out = new FileOutputStream("output.xlsx")) {
workbook.write(out);
} finally {
workbook.close();
((SXSSFWorkbook) workbook).dispose(); // 清理临时文件
}
请确保在处理大文件时使用 SXSSFWorkbook
,这样可以有效避免内存溢出的问题,并且正确使用 close()
和 dispose()
方法来释放资源。
也就是如果你使用的是XSSFWorkbook,哪怕调用了close()方法,临时资源也不会自动关闭,这时可以的一个辅助措施是代码显示调用gc,即
System.gc(); // 手动调用垃圾回收器
一.理论分析
在 Apache POI 库中,workbook.dispose()
和 workbook.close()
都是用于释放资源的方法,但它们的用途和实现有所不同。
workbook.close()
:
-
close()
方法通常用于关闭工作簿并释放与之相关的资源。 - 对于
XSSFWorkbook
,close()
方法会关闭工作簿并释放所有与之相关的临时文件和资源。 - 对于
SXSSFWorkbook
,close()
方法会关闭工作簿并释放所有与之相关的临时文件和资源,但不会删除临时文件。
try (FileOutputStream out = new FileOutputStream("output.xlsx")) {
workbook.write(out);
} finally {
workbook.close();
}
workbook.dispose()
:
-
dispose()
方法是 SXSSFWorkbook
特有的方法,用于清理临时文件。 - 在
SXSSFWorkbook
中,dispose()
方法会删除所有与工作簿相关的临时文件,确保不会留下任何临时文件。 -
dispose()
方法通常在 close()
方法之后调用,以确保所有资源都被正确释放。
二.操作
try (FileOutputStream out = new FileOutputStream("output.xlsx")) {
workbook.write(out);
} finally {
workbook.close();
workbook.dispose(); // 清理临时文件
}
三.总结:
-
workbook.close()
用于关闭工作簿并释放资源。 -
workbook.dispose()
用于清理 SXSSFWorkbook
的临时文件。
在实际使用中,通常会先调用 close()
方法来关闭工作簿,然后调用 dispose()
方法来清理临时文件。这样可以确保所有资源都被正确释放,避免资源泄漏。
四.扩展阅读
XSSFWorkbook 怎样优雅使用不造成内存溢出?
XSSFWorkbook
是 Apache POI 库中用于处理 Excel 文件(.xlsx)的类。由于 .xlsx
文件是基于 XML 的,处理大文件时可能会导致内存占用过高,甚至内存溢出。为了优雅地使用 XSSFWorkbook
并避免内存溢出,可以采用以下几种策略:
- 使用 SXSSFWorkbook:
SXSSFWorkbook
是XSSFWorkbook
的一个流式版本,它通过写入临时文件来减少内存占用。这是处理大 Excel 文件的首选方法。
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.ss.usermodel.Row;
public class ExcelWriter {
public static void main(String[] args) throws Exception {
SXSSFWorkbook workbook = new SXSSFWorkbook(100); // 保持最近 100 行在内存中
SXSSFSheet sheet = workbook.createSheet("Sheet1");
for (int rowNum = 0; rowNum < 1000; rowNum++) {
Row row = sheet.createRow(rowNum);
for (int cellNum = 0; cellNum < 10; cellNum++) {
row.createCell(cellNum).setCellValue("Cell " + cellNum);
}
}
try (FileOutputStream out = new FileOutputStream("output.xlsx")) {
workbook.write(out);
} finally {
workbook.dispose(); // 清理临时文件
}
}
}
- 分批处理数据: 如果数据是从数据库或其他数据源读取的,可以分批读取和写入数据,而不是一次性加载所有数据。
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.ss.usermodel.Row;
public class ExcelWriter {
public static void main(String[] args) throws Exception {
SXSSFWorkbook workbook = new SXSSFWorkbook(100);
SXSSFSheet sheet = workbook.createSheet("Sheet1");
int batchSize = 100;
int totalRows = 1000;
for (int startRow = 0; startRow < totalRows; startRow += batchSize) {
int endRow = Math.min(startRow + batchSize, totalRows);
for (int rowNum = startRow; rowNum < endRow; rowNum++) {
Row row = sheet.createRow(rowNum);
for (int cellNum = 0; cellNum < 10; cellNum++) {
row.createCell(cellNum).setCellValue("Cell " + cellNum);
}
}
// 可以在这里进行一些清理操作,比如释放不再需要的对象
}
try (FileOutputStream out = new FileOutputStream("output.xlsx")) {
workbook.write(out);
} finally {
workbook.dispose();
}
}
}
- 手动管理内存: 在处理大文件时,可以手动调用垃圾回收器来帮助释放内存。
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.ss.usermodel.Row;
public class ExcelWriter {
public static void main(String[] args) throws Exception {
SXSSFWorkbook workbook = new SXSSFWorkbook(100);
SXSSFSheet sheet = workbook.createSheet("Sheet1");
for (int rowNum = 0; rowNum < 1000; rowNum++) {
Row row = sheet.createRow(rowNum);
for (int cellNum = 0; cellNum < 10; cellNum++) {
row.createCell(cellNum).setCellValue("Cell " + cellNum);
}
if (rowNum % 100 == 0) {
System.gc(); // 手动调用垃圾回收器
}
}
try (FileOutputStream out = new FileOutputStream("output.xlsx")) {
workbook.write(out);
} finally {
workbook.dispose();
}
}
}
通过使用 SXSSFWorkbook
、分批处理数据和手动管理内存,可以有效地避免使用 XSSFWorkbook
时可能出现的内存溢出问题。