Java8+Tomcat8 Spring应用上传文件触发SIGBUS错误求助
文件上传时触发SIGBUS错误的排查与解决
问题概述
基于Java 8(8u201)和Tomcat 8的Spring应用,原本运行正常,近期在文件上传流程中触发SIGBUS崩溃,错误发生在Files.copy执行阶段(此前System.out.println可正常输出目标路径),重构代码后问题仍存在。
错误日志片段
# # A fatal error has been detected by the Java Runtime Environment: # # SIGBUS (0x7) at pc=0x00007f5c1f9dc200, pid=367, tid=0x00007f5ba8c4a640 # # JRE version: Java(TM) SE Runtime Environment (8.0_201-b09) (build 1.8.0_201-b09) # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.201-b09 mixed mode linux-amd64 compressed oops) # Problematic frame: # V [libjvm.so+0x7dc200] # # Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again # --------------- T H R E A D --------------- Current thread (0x00007f5c187fc000): JavaThread "http-nio-8443-exec-9" daemon [_thread_in_vm, id=417, stack(0x00007f5ba8b4b000,0x00007f5ba8c4b000)] siginfo: si_signo: 7 (SIGBUS), si_code: 2 (BUS_ADRERR), si_addr: 0x00007f5b662127aa
相关代码片段
Path copyLocation = Paths.get(uploadDir + File.separator + StringUtils.cleanPath(fileName)); System.out.println("copyLocation of Files:" + copyLocation); Files.copy(file.getInputStream(), copyLocation, StandardCopyOption.REPLACE_EXISTING);
原因分析
SIGBUS(BUS_ADRERR)表示进程尝试访问的内存地址有效,但未映射到物理内存,结合场景可能的触发点:
- 磁盘IO异常:上传目标目录或Tomcat临时目录磁盘满、权限不足,导致文件写入时底层内存映射失败
- JVM NIO bug:Java 8u201存在NIO相关的已知bug,在处理文件流复制时可能触发底层JVM崩溃
- 临时文件异常:Tomcat处理上传时生成的临时文件被意外清理(比如系统定时清理临时目录),导致
file.getInputStream()指向的文件失效,后续复制操作触发内存错误
解决方案
1. 检查磁盘状态
- 检查上传目标目录和Tomcat临时目录(默认
CATALINA_BASE/temp)的剩余磁盘空间,确保有足够存储容量 - 验证目录的读写权限,确保Tomcat运行用户对两个目录拥有完整的读写权限
2. 调整Tomcat上传配置
- 在
conf/web.xml中配置文件上传参数,避免临时文件处理异常:<multipart-config> <max-file-size>10485760</max-file-size> <!-- 10MB --> <max-request-size>20971520</max-request-size> <!-- 20MB --> <file-size-threshold>4096</file-size-threshold> <!-- 4KB --> </multipart-config> - 启动Tomcat时显式指定安全的临时目录,避免系统临时目录被清理:
# 添加到Tomcat启动脚本 JAVA_OPTS="$JAVA_OPTS -Djava.io.tmpdir=/path/to/your/tomcat-temp"
3. 升级JVM版本
Java 8u201存在多个NIO相关的已知缺陷,建议升级到Java 8u301及以上的稳定补丁版本,修复底层bug。
4. 替换文件复制实现
绕开Files.copy的NIO内存映射逻辑,改用传统的流复制方式:
Path copyLocation = Paths.get(uploadDir + File.separator + StringUtils.cleanPath(fileName)); System.out.println("copyLocation of Files:" + copyLocation); // 替换原Files.copy代码 try (InputStream inputStream = file.getInputStream(); FileOutputStream outputStream = new FileOutputStream(copyLocation.toFile())) { byte[] buffer = new byte[8192]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } } catch (IOException e) { // 加入业务异常处理逻辑 log.error("文件复制失败", e); throw new RuntimeException("文件上传失败", e); }
5. 启用Core Dump深入排查
执行以下命令后重启应用,下次崩溃时生成core dump文件,结合gdb和jstack工具进一步定位底层问题:
ulimit -c unlimited
内容的提问来源于stack exchange,提问作者Bigyan Kharel
相关产品推荐
相关产品推荐

