如何调试 JVM 中发生的 Segfaults(当 JVM 运行我的代码)时?

2022-09-03 00:13:07

我的Java应用程序已经开始定期崩溃,有一个SIGSEGV和堆栈数据转储以及文本文件中的信息负载。

我已经在gdb中调试了C程序,并且从IDE中调试了Java代码。我不确定如何在正在运行的Java程序中处理类似C的崩溃。

我假设我在这里没有看到JVM错误。其他Java程序运行良好,Sun的JVM可能比我的代码更稳定。但是,我不知道我怎么会用Java代码导致segfaults。肯定有足够的内存可用,当我上次检查探查器时,堆使用率约为50%,偶尔峰值约为80%。是否有任何可以调查的启动参数?在处理这样的错误时,什么是好的清单?

虽然到目前为止我无法可靠地重现事件,但它似乎也不是完全随机发生的,因此测试并非完全不可能。

ETA:一些血腥的细节

(我正在寻找一种通用方法,因为实际问题可能非常具体。不过,我已经收集了一些信息,这些信息可能有一定的价值。

不久前,我在升级CI服务器后遇到了类似的麻烦(请参阅此处了解更多详细信息),但这次修复(设置)没有帮助。-XX:MaxPermSize

进一步的调查显示,在崩溃日志文件中,标记为“当前线程”的线程从来都不是我的线程之一,而是称为“VMThread”的线程或称为“GCTaskThread”的线程 - 如果是后者,则另外标有注释“(已退出)”,如果是前者,则GCTaskThread不在列表中。这使我认为问题可能在GC操作结束时。


答案 1

我假设我在这里没有看到JVM错误。其他Java程序运行良好,Sun的JVM可能比我的代码更稳定。

我不认为你应该做出这样的假设。如果不使用JNI,您应该无法编写导致SIGSEGV的Java代码(尽管我们知道它会发生)。我的观点是,当它发生时,它要么是JVM中的错误(并非闻所未闻),要么是某些JNI代码中的错误。如果你在自己的代码中没有任何JNI,这并不意味着你没有使用一些库,所以要寻找它。当我以前看到这种问题时,它是在图像处理库中。如果罪魁祸首不在你自己的JNI代码中,你可能无法“修复”这个错误,但你仍然可以解决它。

首先,您应该在同一平台上获取备用 JVM 并尝试重现它。您可以尝试以下替代方法之一

如果无法重现它,则可能是 JVM 错误。由此,您可以强制使用特定的JVM或搜索错误数据库,使用您了解如何重现它的知识,并可能获得建议的解决方法。(即使您可以重现它,许多JVM实现只是对Oracle的Hotspot实现的调整,因此它可能仍然是JVM错误。

如果您可以使用备用 JVM 重现它,则错误可能是您有一些 JNI 错误。查看您正在使用的库以及它们可能进行的本机调用。有时,对于同一库,有替代的“纯 Java”配置或 jar 文件,或者执行几乎相同操作的替代库。

祝你好运!


答案 2

除非您有本机代码,否则以下内容几乎肯定是无用的。但是,这里开始了。

  1. 在java调试器中启动java程序,在可能的sigsegv之前有断点。
  2. 使用 ps 命令获取 java 的进程 id。
  3. gdb /usr/lib/jvm/sun-java6/bin/java processid
  4. 确保 gdb “handle” 命令设置为在 SIGSEGV 上停止
  5. 在 Java 调试器中从断点继续。
  6. 等待爆炸。
  7. 使用 gdb 进行调查

如果你真的设法让JVM在没有任何自己的原生代码的情况下采取sigsegv,那么你不太可能理解你接下来会看到什么,你能做的最好的事情就是把一个测试用例推到一个bug报告上。


推荐