【发布时间】:2010-11-07 11:35:26
【问题描述】:
这是我几个月来一直试图追查的一个问题。我有一个正在运行的 java 应用程序,它处理 xml 提要并将结果存储在数据库中。出现了间歇性的资源问题,很难追踪。
背景: 在生产箱(问题最明显)上,我没有特别好的访问箱,并且无法让 Jprofiler 运行。那个盒子是一个 64 位四核、8GB 机器,运行 centos 5.2、tomcat6 和 java 1.6.0.11。它从这些 java-opts 开始
JAVA_OPTS="-server -Xmx5g -Xms4g -Xss256k -XX:MaxPermSize=256m -XX:+PrintGCDetails -
XX:+PrintGCTimeStamps -XX:+UseConcMarkSweepGC -XX:+PrintTenuringDistribution -XX:+UseParNewGC"
技术栈如下:
- Centos 64 位 5.2
- Java 6u11
- 雄猫 6
- 弹簧/WebMVC 2.5
- 休眠 3
- 石英 1.6.1
- DBCP 1.2.1
- Mysql 5.0.45
- Ehcache 1.5.0
- (当然还有许多其他依赖项,尤其是 jakarta-commons 库)
最接近重现该问题的方法是 32 位机器,其内存要求较低。我确实可以控制。我已经用 JProfiler 对它进行了彻底的探索,并修复了许多性能问题(同步问题、预编译/缓存 xpath 查询、减少线程池、删除不必要的休眠预取,以及处理过程中过分热心的“缓存预热”)。
在每种情况下,分析器都显示它们出于某种原因占用了大量资源,并且一旦发生更改,这些就不再是主要的资源消耗。
问题: JVM 似乎完全忽略了内存使用设置,填满了所有内存并变得无响应。这对于面向客户的终端来说是一个问题,他们希望定期轮询(5 分钟基础和 1 分钟重试),对于我们的运营团队来说也是一个问题,他们会不断收到通知说盒子已经无响应并且必须重新启动它。这个盒子上没有其他重要的东西。
问题似乎是垃圾收集。我们正在使用 ConcurrentMarkSweep(如上所述)收集器,因为原始 STW 收集器导致 JDBC 超时并且变得越来越慢。日志显示,随着内存使用量的增加,即开始引发 cms 故障,并返回到原来的 stop-the-world 收集器,然后似乎无法正确收集。
但是,使用 jprofiler 运行时,“运行 GC”按钮似乎可以很好地清理内存,而不是显示占用空间增加,但是由于我无法将 jprofiler 直接连接到生产盒,并且解决已验证的热点似乎不是工作时,我只剩下调整垃圾收集的巫术了。
我尝试过的:
- 分析和修复热点。
- 使用 STW、Parallel 和 CMS 垃圾收集器。
- 以 1/2,2/4,4/5,6/6 增量的最小/最大堆大小运行。
- 以 256M 增量运行 permgen 空间,最高可达 1Gb。
- 以上的许多组合。
- 我还查阅了 JVM [调优参考](http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html),但找不到任何解释此行为的内容或任何示例_which_ 在这种情况下使用的调整参数。
- 我也(不成功)在离线模式下尝试了 jprofiler,与 jconsole、visualvm 连接,但我似乎找不到任何可以解释我的 gc 日志数据的东西。
不幸的是,问题也是零星出现的,似乎无法预测,可以运行几天甚至一周都没有任何问题,或者一天可以失败40次,而我似乎唯一能做到的持续的问题是垃圾收集正在运行。
谁能给点建议:
a) 为什么 JVM 在配置为最大小于 6 时使用 8 个物理 gig 和 2 GB 交换空间。
b) 对 GC 调整的参考,它实际上解释或给出了合理的示例,说明何时以及使用哪种设置来使用高级集合。
c)对最常见的 java 内存泄漏的引用(我理解无人认领的引用,但我的意思是在库/框架级别,或者在数据结构中更inherenet,如哈希图)。
感谢您提供的所有见解。
编辑
埃米尔 H:
1) 是的,我的开发集群是生产数据的镜像,一直到媒体服务器。主要区别在于 32/64 位和可用的 RAM 量,我不能很容易地复制它们,但代码、查询和设置是相同的。
2) 有一些遗留代码依赖于 JaxB,但在重新排序作业以避免调度冲突时,我通常会取消该执行,因为它每天运行一次。主解析器使用调用 java.xml.xpath 包的 XPath 查询。这是一些热点的来源,一个是查询没有被预编译,两个对它们的引用是硬编码的字符串。我创建了一个线程安全缓存(hashmap)并将对 xpath 查询的引用分解为最终的静态字符串,从而显着降低了资源消耗。查询仍然是处理的很大一部分,但应该是因为这是应用程序的主要职责。
3) 另外需要注意的是,另一个主要消费者是来自 JAI 的图像操作(重新处理来自提要的图像)。我不熟悉 java 的图形库,但据我发现它们并不是特别容易泄漏。
(感谢到目前为止的答案,伙计们!)
更新:
我能够使用 VisualVM 连接到生产实例,但它禁用了 GC 可视化/run-GC 选项(尽管我可以在本地查看它)。有趣的是:VM 的堆分配遵循 JAVA_OPTS,实际分配的堆舒适地坐在 1-1.5 gigs 上,似乎没有泄漏,但箱级监控仍然显示泄漏模式,但它是未反映在 VM 监控中。这个盒子上没有其他东西在运行,所以我很难过。
【问题讨论】:
-
您是否使用真实世界的数据和真实世界的数据库进行测试?最好是生产数据的副本?
-
+1 - 这是我读过的最好的问题之一。我希望我能在帮助方面提供更多帮助。我会回到这个,看看是否有人有聪明的说法。
-
另外,您使用的是什么 XML 解析器?
-
您查看分配的 ByteBuffers 的数量以及分配它们的人吗?
-
查看这个答案:stackoverflow.com/a/35610063,它有关于 Java 原生内存泄漏的详细信息。
标签: java memory-leaks garbage-collection profiling