文档章节

zip文件操作导致JVM crash

囚兔
 囚兔
发布于 2017/06/18 13:40
字数 1116
阅读 546
收藏 0

1. 概况

    程序运行操作系统: CentOS6.5 64bit

    JDK版本:7

2. 测试

2.1 准备测试程序

测试程序很简单,就一个类一个main函数,大概流程:

    先从参数中读取 获取zip文件的时间间隔interval,再从参数中获取zip文件路径。再通过ZipFile类的api来从zip文件中获取文件的全路径名。每次获取一个文件sleep interval时间,便于测试。

代码如下:

    /**
     * Usage: App <interval in ms to get entry> <zip file path>
     * @param args
     */
    public static void main( String[] args ) {
        String arg0 = args[0];
        String arg1 = args[1];

        Long interval = Long.valueOf(arg0);
        System.out.println("interval = " + interval);

        String filename = arg1;
        System.out.println("filename = " + filename);

        ZipFile zipFile = null;
        try {
            zipFile = new ZipFile(filename);
            Enumeration<? extends ZipEntry> entries = zipFile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                System.out.println(entry.getName());
                try {
                    Thread.sleep(interval);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (zipFile != null) {
                try {
                    zipFile.close();
                } catch (IOException e) {
                }
            }
        }

    }

2.2 开始测试

先在/tmp目录下放置一个用于测试的test.zip的压缩文件

将程序打包到服务器,执行如下命令:

java -classpath $CLASSPATH com.spiro.test.App 5000 /tmp/test.zip > $LOG_HOME/app.log

app.log输出:

interval = 5000
filename = /tmp/test.zip
frontend/
frontend/gulpfile.js
frontend/node_modules/
frontend/node_modules/.bin/
frontend/node_modules/.bin/browser-sync
frontend/node_modules/.bin/browser-sync.cmd
frontend/node_modules/.bin/gulp

紧接着重新开一个终端执行如下命令:

echo "abcd" > /tmp/test.zip

将test.zip文件的内容重置修改

app.log则输出:

interval = 5000
filename = /tmp/test.zip
frontend/
frontend/gulpfile.js
frontend/node_modules/
frontend/node_modules/.bin/
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGBUS (0x7) at pc=0x00007fa6701cb7b2, pid=7262, tid=140352832034560
#
# JRE version: OpenJDK Runtime Environment (7.0_141-b02) (build 1.7.0_141-mockbuild_2017_05_09_14_20-b00)
# Java VM: OpenJDK 64-Bit Server VM (24.141-b02 mixed mode linux-amd64 compressed oops)
# Derivative: IcedTea 2.6.10
# Distribution: CentOS release 6.9 (Final), package rhel-2.6.10.1.el6_9-x86_64 u141-b02
# Problematic frame:
# C  [libzip.so+0x47b2]
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# /tmp/jvm-7262/hs_error.log
#
# If you would like to submit a bug report, please include
# instructions on how to reproduce the bug and visit:
#   http://icedtea.classpath.org/bugzilla
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.

jps查看进程已经消失。

根据日志提示,jvm dump文件输出到文件/tmp/jvm-7262/hs_error.log,

查看栈:

Stack: [0x00007fa670a25000,0x00007fa670b26000],  sp=0x00007fa670b24650,  free space=1021k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [libzip.so+0x47b2]
C  [libzip.so+0x4dc8]  ZIP_GetNextEntry+0x48
j  java.util.zip.ZipFile.getNextEntry(JI)J+0
j  java.util.zip.ZipFile.access$500(JI)J+2
j  java.util.zip.ZipFile$1.nextElement()Ljava/util/zip/ZipEntry;+54
j  java.util.zip.ZipFile$1.nextElement()Ljava/lang/Object;+1
j  com.spiro.test.App.main([Ljava/lang/String;)V+100
v  ~StubRoutines::call_stub
V  [libjvm.so+0x60ea4e]
V  [libjvm.so+0x60d5e8]
V  [libjvm.so+0x61e9c7]
V  [libjvm.so+0x632fac]
C  [libjli.so+0x34c5]

可以看到java代码定位在

private static native long getNextEntry(long jzfile, int i);

至此重现了问题。

2.3. 问题解释

通过查询资料,这个跟mmap的linux操作系统机制有关,大致意识是:mmap机制通过将文件映射到内存,这样可以提高文件的访问效率,但是一旦来读取的过程中,文件被修改了,就可能导致错误,从而导致jvm crash。

https://bugs.openjdk.java.net/browse/JDK-8160933

网上给的解决方案中有通过在jvm参数中加入 -Dsun.zip.disableMemoryMapping=true选项,禁用mmap机制,我们下面就来看看加上这个选项的效果;

2.4. 禁用mmap测试

执行:

java -Dsun.zip.disableMemoryMapping=true 
-classpath $CLASSPATH com.spiro.test.App 5000 /tmp/test.zip > $LOG_HOME/app.log

app.log输出:

frontend/
frontend/gulpfile.js
frontend/node_modules/
frontend/node_modules/.bin/
frontend/node_modules/.bin/browser-sync
frontend/node_modules/.bin/browser-sync.cmd
frontend/node_modules/.bin/gulp

执行:

echo "abcd" > test.zip

发现进程会继续执行,并在一段时间后会抛Error异常:

Exception in thread "main" java.util.zip.ZipError: jzentry == 0,
 jzfile = 140689976146736,
 total = 8313,
 name = /tmp/test.zip,
 i = 62,
 message = null
        at java.util.zip.ZipFile$1.nextElement(ZipFile.java:505)
        at java.util.zip.ZipFile$1.nextElement(ZipFile.java:483)
        at com.spiro.test.App.main(App.java:38)

2.5 再次解释

在禁用了mmap后,进程没有crash,而是在一段时间后抛了异常,然后退出进程。

禁用mmap后,文件没有映射到内存,而是程序预先加载一部分数据到内存后继续读取,后文件数据变化后,才发生异常错误。这还只是猜测,后续有空再继续研究。

3. 总结

可以看到jvm crash的根源就在开启mmap机制后,zip文件在读取过程中被修改了。

解决的方法有两种:

    1. 从代码逻辑上控制zip文件在操作过程中,不要被其他逻辑修改。

    2. 在jvm启动参数中加入-Dsun.zip.disableMemoryMapping=true 。

但是个人觉得2这种方式指标不治本,问题根源还在于文件资源共享访问时要做控制。

© 著作权归作者所有

囚兔

囚兔

粉丝 41
博文 92
码字总数 51550
作品 1
南京
程序员
私信 提问
利用 Java dump 进行 JVM 故障诊断

引言 对于大型 java 应用程序来说,再精细的测试都难以堵住所有的漏洞,即便我们在测试阶段进行了大量卓有成效的工作,很多问题还是会在生产环境下暴露出来,并且很难在测试环境中进行重现。...

candies
2014/03/03
446
0
java fatal error log

翻译于《Troubleshooting Guide for JavaSE6 with HotSpotVM》 1. 生成位置 -XX:ErrorFile=/fullpath/file,file里可以包含%p表示进程id。如果没声明,默认的名字是hserrpid.log,保存在进程...

yingtju
2018/06/29
0
0
Java 文件类操作API与IO编程基础知识

阅读目录: https://www.w3cschool.cn/java/java-io-file.html Java 文件 Java 输入流 Java 输出流 Java 阅读器和写入器 Java 随机访问 Java 标准流 Java Zip Jar Java 缓冲通道 Java 文件路...

boonya
10/23
44
0
JRE(version: 6.0_18-b07)的JVM自动挂掉

有一次服务器jvm crash,无任何异常信息。后来想想不对啊,除非是人为的将java的进程kill掉,要不然不可能没有错误日志的,后来突然想起上次价格行情做性能测 试时,当jvm crash掉之后,是在T...

黄平俊
2011/07/18
17.4K
3
【转】Java Crash原因汇总

如果是Java进程不知道什么原因退出或被杀死,想要分析具体原因,一般来说分下面几步: 1. 拿到Java应用程序的日志文件 2. 查找JVM的致命错误日志 3. 查找操作系统的core dump文件 4. 使用Dtr...

mj4738
2011/12/04
742
0

没有更多内容

加载失败,请刷新页面

加载更多

Kafka实战(五) - 核心API及适用场景全面解析

1 四个核心API ● Producer API 允许一个应用程序发布一串流式的数据到一个或者多个Kafka topic。 ● Consumer API 允许一个应用程序订阅一个或多个topic ,并且对发布给他们的流式数据进行处...

JavaEdge
47分钟前
9
0
实现线程的第三种方式——Callable & Future

Callable Runnable 封装一个异步运行的任务, 可以把它想象成为一个没有参数和返回值的异步方 法。Callable 与 Runnable 类似, 但是有返回值。Callable 接口是一个参数化的类型, 只有一 个...

ytuan996
今天
11
0
OSChina 周六乱弹 —— 不要摁F了!

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @巴拉迪维 : 朴树写的词曲都给人一种莫名的失落感,不过这首歌他自己却没有唱,换成赵传这种高音阶嘶喊的确很好,低沉但却有力,老男人的呐喊...

小小编辑
今天
12
0
Android Binder机制 - interface_cast和asBinder讲解

研究Android底层代码时,尤其是Binder跨进程通信时,经常会发现interface_cast和asBinder,很容易被这两个函数绕晕,下面来讲解一下: interface_cast 下面根据下述ICameraClient例子进行分析...

天王盖地虎626
昨天
13
0
计算机实现原理专题--存储器的实现(二)

计算机实现原理专题--存储器的实现(一)中描述了一种可以记住输入端变化的装置。现需要对其功能进行扩充,我们将上面的开关定义为置位,下面的开关定义为复位,然后需要增加一个保持位,当保...

FAT_mt
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部