You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java 8+POI写入Excel时频繁JVM崩溃,求代码级解决方案

Apache POI 3.12 + Java 8 写入Excel时JVM崩溃(libzip.so相关)

问题详情

使用Apache POI 3.12、Java 8及Tomcat服务器写入Excel文件时,频繁触发JVM崩溃,生成的hs_err_pid错误日志如下:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGBUS (0x7) at pc=0x00007f16e02d1ec0, pid=1135, tid=0x00007f1595ef8700
#
# JRE version: OpenJDK Runtime Environment (8.0_352-b08) (build 1.8.0_352-8u352-ga-1~18.04-b08)
# Java VM: OpenJDK 64-Bit Server VM (25.352-b08 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C  [libzip.so+0x4ec0]
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.

------
Stack: [0x00007f1595df8000,0x00007f1595ef9000],  sp=0x00007f1595ef5900,  free space=1014k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [libzip.so+0x4ec0]
C  [libzip.so+0x5b6e]
C  [libzip.so+0x3b25]  Java_java_util_zip_ZipFile_getEntry+0x85
J 91  java.util.zip.ZipFile.getEntry(J[BZ)J (0 bytes) @ 0x00007f16cd13ea8e [0x00007f16cd13e9c0+0xce]
J 123871 C2 java.util.zip.ZipFile.getInputStream(Ljava/util/zip/ZipEntry;)Ljava/io/InputStream; (304 bytes) @ 0x00007f16d6716944 [0x00007f16d67165c0+0x384]
J 124037 C2 org.apache.poi.openxml4j.opc.PackagePart.getInputStream()Ljava/io/InputStream; (44 bytes) @ 0x00007f16cda4bd00 [0x00007f16cda4bc60+0xa0]
J 103326 C1 org.apache.poi.POIXMLProperties.<init>(Lorg/apache/poi/openxml4j/opc/OPCPackage;)V (212 bytes) @ 0x00007f16d6af7374 [0x00007f16d6af6300+0x1074]
J 100022 C1 org.apache.poi.POIXMLDocument.getProperties()Lorg/apache/poi/POIXMLProperties; (40 bytes) @ 0x00007f16cd79ddfc [0x00007f16cd79dce0+0x11c]
J 67174 C1 org.apache.poi.POIXMLDocument.write(Ljava/io/OutputStream;)V (35 bytes) @ 0x00007f16cd89404c [0x00007f16cd893a80+0x5cc]
J 65014 C1 com.bulk.helper.ExcelUtility.saveWorkBook(Lorg/apache/poi/ss/usermodel/Workbook;Ljava/lang/String;Ljava/lang/String;)V (89 bytes) @ 0x00007f16cd543754 [0x00007f16cd542ea0+0x8b4]
J 71211 C1 com.bulk.helper.ExcelAccess.save_userfile(Ljava/lang/String;Ljava/lang/String;)V (14 bytes) @ 0x00007f16ce6e46ec [0x00007f16ce6e4640+0xac]
J 93237 C1

相关业务代码:

// 调用类
accessFile.save_newFile(---);

// 通用工具类
@Override
public void save_userfile(String filePath , String fileName) {
   utility.saveWorkBook(userWorkbook,filePath,fileName);
}

public void saveWorkBook(Workbook workbook ,String filePath, String fileName){
    try
    {
        File directory = new File(filePath);
        if (!directory.exists()) {
            directory.mkdir();
        }
        File file = new File(directory,fileName);
        OutputStream out = new FileOutputStream(file);
        workbook.write(out);
        out.flush();
        out.close();
        workbook.close();
        
    }catch(Exception e){
        LOG.error(Constants.ERROR_STRING,e);
    }
}

已知添加-Dsun.zip.disableMemoryMapping=true参数可解决,但会增加磁盘IO影响性能,希望通过Java 8代码层面直接修复。


代码层面解决方案

1. 规避POIXMLProperties读取的Zip内存映射问题

崩溃栈显示问题出在POIXMLProperties初始化时调用ZipFile的native方法,这是因为Apache POI 3.12处理OOXML文档时,默认使用Java ZipFile的内存映射机制,而Java 8的libzip.so存在相关bug。可通过修改写入逻辑跳过不必要的属性读取:

public void saveWorkBook(Workbook workbook ,String filePath, String fileName){
    try
    {
        File directory = new File(filePath);
        if (!directory.exists()) {
            directory.mkdirs(); // 替换mkdir为mkdirs,支持多级目录创建
        }
        File file = new File(directory,fileName);
        
        // 针对XSSFWorkbook(OOXML格式)处理属性问题
        if (workbook instanceof XSSFWorkbook) {
            XSSFWorkbook xssfWorkbook = (XSSFWorkbook) workbook;
            OPCPackage pkg = xssfWorkbook.getPackage();
            // 清空核心属性,避免触发ZipFile内存映射读取
            pkg.getPackageProperties().setCreatorProperty(null);
            pkg.getPackageProperties().setLastModifiedByProperty(null);
            // 设置为只读模式,跳过属性写入流程
            pkg.setReadOnly(true);
        }
        
        // 使用try-with-resources自动关闭流,避免资源泄漏
        try (OutputStream out = new FileOutputStream(file)) {
            workbook.write(out);
        } finally {
            workbook.close();
        }
    }catch(Exception e){
        LOG.error(Constants.ERROR_STRING,e);
    }
}

2. 替换Java原生Zip实现为Apache Commons Compress

使用Apache Commons Compress的ZipArchive系列类替代Java原生Zip实现,避开libzip.so的bug。需先引入依赖:

<!-- Maven依赖,选择兼容Java 8的版本 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-compress</artifactId>
    <version>1.21</version>
</dependency>

自定义XSSFWorkbook写入逻辑:

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.openxml4j.opc.PackagePart;

public void saveXssfWorkbook(XSSFWorkbook workbook, File file) throws IOException {
    try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream(file)) {
        zos.setUseZip64(ZipArchiveOutputStream.Zip64Mode.AsNeeded);
        // 遍历Workbook所有部件,写入到Zip包
        for (PackagePart part : workbook.getPackage().getParts()) {
            ZipArchiveEntry entry = new ZipArchiveEntry(part.getPartName().getName());
            entry.setSize(part.getSize());
            zos.putArchiveEntry(entry);
            try (InputStream is = part.getInputStream()) {
                IOUtils.copy(is, zos);
            }
            zos.closeArchiveEntry();
        }
    }
    workbook.close();
}

3. 升级Apache POI版本(推荐)

Apache POI 3.12是2015年的老旧版本,后续版本(如3.17+、4.1.2)已针对Java 8的ZipFile问题做了修复,升级到兼容Java 8的最新稳定版本,可从根源解决问题,同时获得更多bug修复和性能优化。


内容的提问来源于stack exchange,提问作者JAK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 21:05:21