我们只需4/30即可调整Futureestack注册。条款和条件适用。 现在注册

我们如何诊断为JVM崩溃

7分钟阅读

可怕的Java虚拟机(JVM)崩溃:这不是很常见,但如果您在Java应用程序上工作,您可能会知道恢复难度是多么困难。我们肯定是这样做的。

数据摄取管道新的遗物浏览器应用是一种高吞吐量,多JVM,分布式应用程序,我们已经建立了充分的警报。但是有一天,所有的JVM都会在同一时间戒掉。当警报出去的时候,我认为这不定是一个错误。来自我们新的遗物监控工具的图表看起来正常,直到它们只是消失!

幸运的是,在此事件中没有客户数据受到损害。在New Relic,我们非常重视数据完整性,所有短期数据都会在任何可能的地方进行备份。所以我们尽快让系统恢复运行,并立即开始对事故的根本原因进行诊断。

但是你甚至如何开始诊断这样的问题?你需要知道为什么JVM崩溃,JVM在崩溃时做了什么,所以你看起来在哪里?

别担心,这个故事有个美好的结局。

从操作系统日志开始

在Linux机器上,当JVM终止时通常会有特定的操作系统信号发送到JVM过程,您可以在OS日志中找到。自从我们的应用程序在Red Hat Linux服务器上运行以来,我开始查找的第一个地方在日志中。

不久,我发现了下面这行:

abt -钩子-ccpp:用户0的进程111915 (java)被SIGSEGV - dump内核杀死

对于大多数Java应用程序,内存访问位于堆,线程堆栈和字符串表中的JVM管理内存中。堆是一个存储空间,它存储由Java应用程序所引用的长寿对象运行。线程堆栈将本地范围的对象存储为单个方法或用作参数。字符串表是存储静态字符串和Interned String的特殊区域。

因此,日志中的此行表示分段故障损坏了JVM过程。换句话说,JVM正在尝试访问不属于它的数据,很可能来自堆外。

为了弄清楚为什么分割错误发生了,我得再深入一点。

日志行由Red Hat的自动错误报告工具(Abrt)。ABRT将文件写入/ var / spool / abrt默认情况下,允许在大小上写入最多1GB的文件。我已经安装了Abrt,但是当JVM崩溃时,它没有捕获任何有用的东西,因为数据摄取应用程序的堆远远大于1GB。

然而,这让我意识到,如果再次发生事故,我可以准备一个更好的系统来获得更多的信息。

核心转储怎么样?

因此,我将ABRT配置为将更大文件大小的核心文件崩溃转储写入连接到应用服务器的大型磁盘阵列。核心文件包含更多关于进程失败时发生的事情的细节。

jvm又一次崩溃了。

这次我用了GDB.(这让您可以检查程序为什么停止了)来打开jvm崩溃时捕获的核心文件。使用GDB回溯命令 - 总结程序如何进入其当前状态,一次一帧 - 我看过崩溃时录制的堆栈跟踪的最后几行。

帧#0 - #6看起来没有意义,只是一般的信号处理。框架7是事情变得有趣的地方:

#0 0x00007f1b6626c5f7在__gi_raise(sig = sig @条目= 6)at ../nptl/sysdeps/unix/sysv/linux/raise.c:56 ...#6 <信号处理程序名为>#7 0x00007f1b64c56cd7在ciobjectfactory ::create_new_metadata(metadata *)()来自/opt/java/jdk1.8.0_74/jre/lib/amd64/server/libjvm.so#8 0x00007f1b64c57105在ciobjectfactory :: get_metadata(metadata *)()from / opt / java / jdk1.8.0_74 / jre / lib / amd64 / server / libjvm.so#9 0x00007f1b64c4edd7在cispeculativetrapdata :: translate_from(profiledata const *)()来自/opt/java/jdk1.8.0_74/jre/lib/amd64/server/libjvm.so#10 0x00007f1b64c4f0e8在cimethoddata :: load_extra_data()()来自/opt/java/jdk1.8.0_74/jre/lib/amd64/7/jre/libjvm.so#11 0x00007f1b64c51258在cimethoddata :: load_data()()from /opt / java / jdk1.8.0_74 / jre / lib / amd64 / server / libjvm.so#12 0x00007f1b64c43fb4在cimethod :: method_data()()from /opt/java/jdk1.8.0_74/jre/lib/amd64/server/libjvm.so#13 0x00007f1b65168055在parse :: parse(jvmstate *,cimethod *,float)()来自/opt/java/jdk1.8.0_74/jre/lib/amd64/server/libjvm.so.

框架#7 - #13告诉我足以理解导致JVM崩溃的原因HotSpot编译器在分配元数据时崩溃。

HotSpot编译器负责正常汇编解释代码成为本机方法。当编译器正常工作时,它透明地提高了性能。但是,在这种情况下,它从未分配的内存读取并崩溃了JVM。

当别人也看到它的时候!

此时,我强烈怀疑问题出在JVM中,而不是我们的应用程序中。通过的Oracle Java的错误数据库, 我发现一个问题这与核心文件的堆栈跟踪非常匹配。

这是个好消息!该错误确实在JVM中,而不是在浏览器的数据摄取管道中。我追踪了修复的进度,当它击中时Oracle JDK发布说明,我部署了那个版本的JVM。从那以后,我再没出过车祸。

任何使用Java应用程序的人都知道,JVM崩溃会导致大范围的中断,而且很难诊断。虽然这些问题中的许多都是未解之谜,但只需要一些持续的故障排除,这个故事就有了一个美好的结局。