【问题标题】:How do I debug Segfaults occurring in the JVM when it runs my code?JVM 运行我的代码时如何调试 Segfaults?
【发布时间】:2011-11-07 05:10:26
【问题描述】:

我的 Java 应用程序已经开始定期崩溃,出现 SIGSEGV 和堆栈数据转储以及文本文件中的大量信息。

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

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

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

预计到达时间:一些血腥细节

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

不久前,我在升级 CI 服务器后遇到了类似的问题(有关详细信息,请参阅 here),但这次修复(设置 -XX:MaxPermSize)并没有帮助。

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

【问题讨论】:

  • 你能得到堆栈跟踪吗?是SEGV在同一个地方吗?我们可以提供更多信息吗?
  • 您的应用程序中有本地代码吗?如果 JVM 允许任何字节码集合(无论该字节码有多么错误)来引发段错误,那么 ipso facto 您正在查看 JVM(或 JRE)错误。
  • @Ed - 我有很多堆栈跟踪,但它是一堵巨大的文字墙。发布什么部分最有用?我主要是在寻找解决此类问题的一般方法,因此我很犹豫在这里转储大量非常具体的信息。
  • @Henning - 也许吧。我有静态编织的类(eclipselink ORM)。事实上,我在介绍它们之后就开始看到问题了(在我进行动态编织之前,结果证明它不起作用)。然而,没有编织类,我有一个完全不同的问题集,很可能掩盖了段错误,所以我不能在这里假设因果关系。
  • @Henning - 我还向-Xbootclasspath 添加了探查器类,但我并不真正了解探查器的工作原理以及引导类路径到底是什么。此外,如果重要的话,我正在调试模式下运行(使用-Xdebug -Xrunjdwp)。

标签: java segmentation-fault


【解决方案1】:

我假设我不是在这里查看 JVM 错误。其他 Java 程序 运行得很好,Sun 的 JVM 可能比我的更稳定 代码。

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

首先,您应该在同一平台上获得一个备用 JVM 并尝试重现它。你可以试试one of these alternatives

如果您无法重现它,则可能是 JVM 错误。从此,您可以使用您所知道的有关如何重现它的知识来授权特定的 JVM 或 search the bug database,并可能获得建议的解决方法。 (即使你可以重现它,许多 JVM 实现只是对 Oracle 的 Hotspot 实现的调整,所以它可能仍然是一个 JVM 错误。)

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

祝你好运!

【讨论】:

  • +1 表示“你可能无法'修复'错误”:所以回答了发帖者的问题“我如何调试 JVM 在运行我的代码时发生的 Segfaults ?”是“不”。
【解决方案2】:

除非您有本机代码,否则以下内容几乎肯定没用。不过,就这样吧。

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

如果您真的设法让 JVM 在没有您自己的任何本机代码的情况下使用 sigsegv,那么您就不太可能理解接下来会看到什么,而您能做的最好的事情就是推送一个测试用例到错误报告中。

【讨论】:

  • 这需要特殊版本的 JVM 吗?从 C 开始,当我想使用 gdb 时,我习惯于使用调试符号重新编译。
  • 根据我的经验,JVM 总是有足够的符号用于回溯。如果你真的打算详细调试它,好吧,去 openJDK 和调试版本。
【解决方案3】:

我在http://www.oracle.com/technetwork/java/javase/crashes-137240.html 找到了一个很好的列表。当我在 GC 期间遇到崩溃时,我将尝试在垃圾收集器之间切换。

我尝试在串行和并行 GC 之间切换(后者是 64 位 Linux 服务器上的默认设置),这只会相应地更改错误消息。

在分析器中进行新的分析后,将最大堆大小从 16G 减少到 10G(这让我的堆使用量在 8G 时趋于平缓)确实导致“虚拟内存”占用量显着降低(16G 而不是 60),但是我什至不知道那是什么意思,而互联网说,没关系。

当前,JVM 在客户端模式下运行(使用-client 启动选项,从而覆盖默认的-server)。到目前为止,还没有崩溃,但性能影响似乎相当大。

【讨论】:

    【解决方案4】:

    如果您有一个核心文件,您可以尝试在其上运行 jstack,这会让您更容易理解 - 请参阅 http://download.oracle.com/javase/6/docs/technotes/tools/share/jstack.html,尽管如果它是 gc 线程中的错误,它可能没有那么有用。

    【讨论】:

      【解决方案5】:

      尝试检查c程序是否导致java crash。使用valgrind知道无效并交叉检查堆栈大小。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-28
        • 2011-01-31
        • 2012-07-22
        • 1970-01-01
        • 2016-09-27
        • 2020-10-12
        相关资源
        最近更新 更多