跟踪 Java 中的内存泄漏/垃圾回收问题
这是我几个月来一直试图追踪的问题。我有一个运行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
- Spring/WebMVC 2.5
- 休眠 3
- 石英 1.6.1
- DBCP 1.2.1
- Mysql 5.0.45
- Ehcache 1.5.0
- (当然还有许多其他依赖项,特别是雅加达共享资源库)
我能得到的最接近重现问题的是内存要求较低的32位机器。我确实可以控制。我已经用JProfiler探测到死,并修复了许多性能问题(同步问题,预编译/缓存xpath查询,减少线程池,删除不必要的休眠预取,以及在处理过程中过度热心的“缓存预热”)。
在每种情况下,分析器都显示这些由于某种原因占用了大量资源,并且一旦更改进入,这些资源就不再是主要资源占用。
问题:JVM似乎完全忽略了内存使用设置,填满了所有内存并变得无响应。对于面向客户端来说,这是一个问题,他们希望定期进行轮询(5 分钟基准和 1 分钟重试),对于我们的运营团队也是如此,他们不断收到通知,指出某个盒子已变得无响应,必须重新启动它。在这个盒子上没有其他重要的运行。
问题似乎是垃圾回收。我们使用 ConcurrentMarkSweep(如上所述)收集器,因为原始 STW 收集器导致 JDBC 超时并变得越来越慢。日志显示,随着内存使用量的增加,即开始抛出cms故障,并踢回原始的停止世界收集器,然后似乎没有正确收集。
但是,使用jprofiler运行,“运行GC”按钮似乎可以很好地清理内存,而不是显示增加的占用空间,但是由于我无法将jprofiler直接连接到生产盒,并且解决经过验证的热点似乎不起作用,因此我留下了盲目调整垃圾收集的巫毒教。
我尝试过:
- 分析和修复热点。
- 使用 STW、并行和 CMS 垃圾回收器。
- 以最小/最大堆大小以 1/2,2/4,4/5,6/6 为增量运行。
- 以 256M 为增量以 256M 为增量运行,最高可达 1Gb。
- 上述的许多组合。
- 我还查阅了 JVM [调优参考](http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html),但找不到任何解释此行为的内容,也无法找到任何在此类情况下要使用的 _that_ 调优参数示例。
- 我也(不成功)在离线模式下尝试了jprofiler,与jconsole,visualvm连接,但我似乎找不到任何可以穿的gc日志数据的东西。
不幸的是,这个问题也偶尔出现,它似乎是不可预测的,它可以运行几天甚至一周而没有任何问题,或者它可以在一天内失败40次,而我唯一能持续抓住的就是垃圾收集正在发挥作用。
任何人都可以给出任何建议:
a)为什么JVM在配置为最大小于6时使用8个物理演出和2 GB的交换空间。
b) 对 GC 调优的引用,它实际上解释或给出了何时以及使用哪种设置的合理示例。
c)对最常见的java内存泄漏的引用(我理解无人认领的引用,但我的意思是在库/框架级别,或者数据结构中更内嵌的东西,如哈希映射)。
感谢您提供的任何和所有见解。
编辑
Emil H:
1)是的,我的开发集群是生产数据的镜像,一直到媒体服务器。主要区别在于32/64位和可用的RAM量,我无法很容易地复制,但代码,查询和设置是相同的。
2)有一些依赖于JaxB的旧代码,但是在重新排序作业以避免调度冲突时,我通常消除了这种执行,因为它每天运行一次。主解析器使用 XPath 查询,这些查询向下调用 java.xml.xpath 包。这是一些热点的来源,其中一个是查询没有预先编译,两个对它们的引用是硬编码的字符串。我创建了一个线程安全缓存(哈希映射),并将对 xpath 查询的引用分解为最终的静态字符串,这大大降低了资源消耗。查询仍然是处理工作的很大一部分,但应该是因为这是应用程序的主要职责。
3)另外一个注意事项,另一个主要消费者是来自JAI的图像操作(从源重新处理图像)。我不熟悉java的图形库,但从我发现它们并不是特别泄漏。
(感谢到目前为止的答案,伙计们!
更新:
我能够使用VisualVM连接到生产实例,但它已经禁用了GC可视化/运行GC选项(尽管我可以在本地查看它)。有趣的是:VM 的堆分配服从JAVA_OPTS,实际分配的堆舒适地位于 1-1.5 gigs,似乎没有泄漏,但机箱级别的监控仍然显示泄漏模式,但它没有反映在 VM 监控中。这个盒子上没有其他东西在运行,所以我很困惑。