【问题标题】:JVM crashes under stress on RHEL 5.2JVM 在 RHEL 5.2 的压力下崩溃
【发布时间】:2011-01-15 21:10:42
【问题描述】:

4 到 24 小时 4 小时到 8 天后,在(当前最新的)tomcat 6.0.24 上运行 Web 应用程序时,我遇到了(当前最新的)jdk 1.6.0.18 崩溃压力测试(30 个线程以 600 万次/天的浏览量访问应用程序)。这是在 RHEL 5.2 (Tikanga) 上。

崩溃报告位于http://pastebin.com/f639a6cf1,崩溃的一致部分是:

  • 正在抛出 SIGSEGV
  • 在 libjvm.so 上
  • eden 空间总是满的 (100%)

JVM 使用以下选项运行:

CATALINA_OPTS="-server -Xms512m -Xmx1024m -Djava.awt.headless=true"

我还使用http://memtest.org/ 测试了内存的硬件问题 48 小时(整个内存的 14 次通过),没有任何错误。

我已启用 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 来检查任何 GC 趋势或空间耗尽,但那里没有任何可疑之处。 GC 和完全 GC 以可预测的时间间隔发生,几乎总是释放相同数量的内存容量。

我的应用程序不直接使用任何本机代码。

对我接下来应该看哪里有什么想法吗?

编辑 - 更多信息

1) 这个JDK中没有客户端虚拟机:

[foo@localhost ~]$ java -version -server
java version "1.6.0_18"
Java(TM) SE Runtime Environment (build 1.6.0_18-b07)
Java HotSpot(TM) 64-Bit Server VM (build 16.0-b13, mixed mode)

[foo@localhost ~]$ java -version -client
java version "1.6.0_18"
Java(TM) SE Runtime Environment (build 1.6.0_18-b07)
Java HotSpot(TM) 64-Bit Server VM (build 16.0-b13, mixed mode)

2) 无法更改操作系统。

3) 我不想更改 JMeter 压力测试变量,因为这可能会隐藏问题。由于我有一个使 JVM 崩溃的用例(当前的压力测试场景),我想修复崩溃而不更改测试。

4) 我在申请中完成了static analysis,但没有出现任何严重问题。

5) 内存不会随时间增长。内存使用量以非常稳定的趋势非常迅速地平衡(启动后),这似乎并不可疑。

6) /var/log/messages 在崩溃之前或期间不包含任何有用的信息

更多信息:忘了提到有一个使用 mod_jk 1.2.28 的 apache (2.2.14) 前端 tomcat。现在我在没有 apache 的情况下运行测试,以防 JVM 崩溃与连接到 JVM(tomcat 连接器)的 mod_jk 本机代码有关。

之后(如果 JVM 再次崩溃)我将尝试从我的应用程序中删除一些组件(缓存、lucene、quartz),稍后将尝试使用码头。由于崩溃目前在 4 小时到 8 天之间的任何时间发生,因此可能需要很长时间才能查明发生了什么。

【问题讨论】:

  • 这个需要去Sun Oracle。
  • @bmargulies:这就是我最初的想法,但后来我读到了stackoverflow.com/questions/1353514/…
  • 假设您使用的是最新的 JDK,您是否尝试过使用 VisualVM 实时研究其行为?我们发现它在调查泄漏方面比第三方配置文件更有效。
  • @Uri:感谢您提到 VisualVM。看起来很有趣。
  • 没有问题。我们对此非常满意,尤其是与我们之前使用过的工具相比。唯一需要花费时间的是加载堆转储。但是您可以让分析器配置内存而不是性能,它实际上会跟踪谁创建了哪些对象——这对于跟踪内存泄漏非常有用。如果您负担得起,请确保增加 VisualVM 的可用堆大小。

标签: java jvm crash segmentation-fault rhel


【解决方案1】:

你有编译器输出吗?即PrintCompilation(如果您感觉特别勇敢,请使用 LogCompilation)。

我通过观察编译器正在做什么来调试这样的案例,最终(这花了很长时间直到灯泡时刻),我意识到我的崩溃是由编译中的特定方法引起的oracle jdbc 驱动。

基本上我会做的是;

  • 打开打印编译
  • 因为它不提供时间戳,所以编写一个脚本来监视该日志文件(例如每秒睡眠并打印新行)并报告方法何时编译(或不编译)
  • 重复测试
  • 检查编译器输出以查看崩溃是否与某些方法的编译相对应
  • 再重复几次,看看是否有规律

如果存在可辨别的模式,则使用 .hotspot_compiler(或 .hotspotrc)使其停止编译有问题的方法,重复测试并查看它是否不会崩溃。显然,在你的情况下,这个过程理论上可能需要几个月的时间。

一些参考资料

我要做的另一件事是系统地更改您正在使用的 gc 算法根据 gc 活动检查崩溃时间(例如,它是否与年轻或旧 gc 相关,TLAB 呢? ?)。您的转储表明您正在使用并行清除,因此请尝试

  • 串行(年轻)收集器(IIRC 可以与并行旧收集器结合使用)
  • ParNew + CMS
  • G1

如果它没有在不同的 GC 算法中重复出现,那么您就知道是这样(并且您没有解决办法,只能更改 GC 算法和/或返回旧 JVM,直到找到该算法的一个版本不不要吹)。

【讨论】:

  • 感谢您让我注意到 PrintCompilation。一定会试试的。
【解决方案2】:

一些想法:

  • 使用不同的 JDK、Tomcat 和/或操作系统版本
  • 稍微修改测试参数,例如25 个主题,每天 720 万次浏览量
  • 监控或分析内存使用情况
  • 调试或调整垃圾收集器
  • 运行静态和动态分析

【讨论】:

    【解决方案3】:

    您尝试过不同的硬件吗?看起来您使用的是 64 位架构。根据我自己的经验,32 位更快、更稳定。也许某处也存在硬件问题。 “4-24 小时之间”的时间很分散,只是一个软件问题。尽管您确实说系统日志没有错误,但我可能会走远。还是觉得值得一试。

    【讨论】:

    • 不能尝试不同的硬件,但我会尝试 32 位 jvm。谢谢
    【解决方案4】:

    你的记忆会随着时间的推移而增长吗?如果是这样,我建议将内存限制更改得更低,以查看内存耗尽时系统是否更频繁地发生故障。

    如果出现以下情况,您能否更快地重现问题:

    • 您减少了 JVM 的可用内存?
    • 您减少了可用的系统资源(即耗尽系统内存导致 JVM 没有足够的资源)
    • 您将用例更改为更简单的模型?

    我使用的主要策略之一是确定导致问题的用例。这可能是一个通用问题,也可能是特定于用例的问题。尝试记录用例的开始和停止,看看您是否可以确定哪些用例更有可能导致问题。如果您将用例分成两半,请查看哪一半失败最快。这可能是导致失败的更常见原因。当然,对每种配置运行几次试验会提高测量的准确性。

    我也知道要么改变服务器做很少的工作,要么循环服务器正在做的工作。一个使您的应用程序代码更难工作,另一个使 Web 服务器和应用程序服务器更难工作。

    祝你好运, 雅各布

    【讨论】:

    • 查看您的跟踪信息,在这种情况下,系统内存应该不是问题。系统日志中是否有任何消息?另外,如果我没看错,看起来您可能有相当多的线程正在运行。在任何给定时间都有大量线程在等待可用的 CPU。我希望用更少的线程实现更快的平均响应时间。
    【解决方案5】:

    尝试将您的 servlet 容器从 Tomcat 切换到 Jetty http://jetty.codehaus.org/jetty/

    【讨论】:

    • 看看JVM还会不会crash?还是完全迁移到码头?
    • 我会完全迁移到 Jetty,只是因为我喜欢过去从 Jetty 看到的东西。然而,我刚刚在谷歌上搜索的最新比较似乎表明 Jetty-6 与 Tomcat-6 的性能相当,尽管 Jetty 确实具有更轻的内存占用。从更有条理的方法来看,只要您的应用程序符合标准,迁移应该不会太难,然后您可以消除容器作为根本原因或验证您的应用程序是根本原因。祝你好运。
    • 感谢您的评论。我的应用程序与所有主要服务器(tomcat、jboss、resin、jetty、glashfish)兼容,因此迁移没有问题。我一定会在码头上进行压力测试。
    【解决方案6】:

    如果我是你,我会这样做:

    • 尝试稍旧的 Tomcat/JVM 版本。你似乎正在运行最新和最伟大的。我会下载两个左右的版本,可能会尝试 JRockit JVM。
    • 在应用程序运行时执行线程转储 (kill -3 java_pid) 以查看完整堆栈。您当前的转储显示许多线程被阻塞 - 但不清楚它们在哪里阻塞(I/O?一些内部锁饥饿?还有什么?)。我什至可能会安排 kill -3 每分钟运行一次,以将任何随机线程转储与崩溃前的线程转储进行比较。
    • 我见过 Linux JDK 死机而 Windows JDK 能够优雅地捕获异常(当时是 StackOverflowException)的情况,因此如果您可以修改代码,请在顶级类的某处添加“catch Throwable”。以防万一。
    • 使用 GC 调整选项。开启/关闭并发 GC,调整 NewSize/MaxNewSize。是的,这不是科学的——而是迫切需要有效的解决方案。更多详情:http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html

    让我们知道这是如何解决的!

    【讨论】:

      【解决方案7】:

      是否可以转而使用 32 位 JVM?我相信这是 Sun 最成熟的产品。

      【讨论】:

        猜你喜欢
        • 2012-03-10
        • 1970-01-01
        • 2014-02-02
        • 2010-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-14
        • 1970-01-01
        相关资源
        最近更新 更多