首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在Java中跟踪内存泄漏/垃圾收集问题

在Java中跟踪内存泄漏/垃圾收集问题
EN

Stack Overflow用户
提问于 2018-03-01 06:55:04
回答 2查看 0关注 0票数 0

这是一个问题,我一直在努力追查几个月。我有一个Java应用程序正在运行,它处理XML提要并将结果存储在数据库中。一直以来,都出现了很难追踪的间歇性资源问题。

背景:在生产盒(问题最明显的地方)上,我对这个盒子没有特别好的访问权限,并且无法运行Jprofler。这个盒子是一个64位四核的机器,8GB的机器运行Centos 5.2,tomcat 6和java 1.6.0.11。它从这些java-opts开始。

代码语言:txt
复制
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
  • Tomcat 6
  • Spring/WebMVC 2.5
  • 冬眠3
  • 石英1.6.1
  • DBCP 1.2.1
  • MySQL 5.0.45
  • Ehcache 1.5.0
  • (当然还有许多其他依赖项,尤其是Jakarta-CommonsLibrary)

我最接近于再现这个问题的是一台32位的机器,它的内存要求较低。我确实可以控制。我已经用JProfiler研究了它,并修复了许多性能问题(同步问题,预编译/缓存XPath查询,减少线程池,消除不必要的Hibernate预取,以及处理过程中过度热心的“缓存升温”)。

在每一种情况下,分析器都会显示出,由于某种原因,它们占用了大量的资源,并且一旦发生变化,这些资源就不再是主要的资源消耗。

问题是:JVM似乎完全忽略了内存使用设置,填充了所有内存,并变得没有响应。这对于客户来说是一个问题,因为他们期望定期投票(5分钟的基础和1分钟的重试),以及我们的运营团队,他们不断地被告知一个盒子已经没有响应,必须重新启动它。在这个盒子上没有其他重要的东西在运行。

问题出现垃圾收集。我们使用ConcurrentMarkSweep(如上所述)收集器,因为最初的STW收集器导致JDBC超时,并且变得越来越慢。日志显示,随着内存使用量的增加,也就是开始抛出cms故障,并返回到原来的停止世界收集器,然后似乎没有正确地收集。

然而,使用jprofler运行时,“RunGC”按钮似乎可以很好地清理内存,而不是显示出越来越大的内存占用,但是由于我无法将jprofler直接连接到生产框,并且解决已验证的热点似乎不起作用,所以我只能用调整垃圾收集盲区的巫术。

我试过的是:

  • 分析和修复热点。
  • 使用STW,并行和CMS垃圾收集器。
  • 以1/2、2/4、4/5、6/6增量的最小/最大堆大小运行。
  • 在256米内运行Permgen空间,最高可增加1GB。
  • 上面的许多组合。
  • 我还咨询了jvm。调谐基准,但是找不到任何解释这种行为的东西,也找不到任何例子_哪种_在这种情况下使用的调优参数。
  • 我也尝试过(不成功)在离线模式下尝试jprofler,与j控制台连接,visual alvm,但我似乎找不到任何东西来干扰我的GC日志数据。

不幸的是,这个问题偶尔也会出现,它似乎是不可预测的,它可以运行几天甚至一周而没有任何问题,或者它一天可能失败40次,而我唯一能持续捕捉到的事情就是垃圾收集出了问题。

是否有人就下列事项提出任何建议:

(A)为什么JVM在配置为小于6的情况下使用8个物理设备和2GB的交换空间。

(B)对GC调优的引用,它实际上解释或提供了使用高级集合的时间和类型设置的合理示例。

(C)对最常见的java内存泄漏的引用(我理解无人声称的引用,但我指的是库/框架级别的引用,或者数据结构中更多的继承内容,如hashmap)。

感谢您所能提供的所有洞察力。

Emil H:

1)是的,我的开发集群是生产数据的镜像,直到媒体服务器。主要的区别是32/64位和可用RAM的数量,我不能很容易地复制,但是代码、查询和设置是相同的。

2)有一些遗留代码依赖于JaxB,但是在重新排序作业以避免调度冲突时,由于它每天运行一次,所以我已经消除了该执行。主解析器使用XPath查询,这些查询调用java.xml.xpath包。这是一些热点的来源,因为一个查询没有被预编译,两个引用是硬编码的字符串。我创建了一个ThreadSafe缓存(Hashmap),并将对XPath查询的引用分解为最终的静态String,这大大降低了资源消耗。查询仍然是处理的很大一部分,但这应该是因为这是应用程序的主要责任。

3)另外一个注意事项,另一个主要使用者是JAI(从提要中重新处理图像)的图像操作。我不熟悉java的图形库,但据我所知,它们并不是特别漏的。

我能够用VisualVM连接到生产实例,但是它禁用了GC可视化/run-GC选项(尽管我可以在本地查看它)。有趣的是:VM的堆分配遵循Java_选项,而实际分配的堆处于1-1.5 G的舒适状态,而且似乎没有泄漏,但是盒子级监视仍然显示泄漏模式,但它没有反映在VM监视中。这个箱子上没有别的东西了,所以我很为难。

EN

回答 2

Stack Overflow用户

发布于 2018-03-01 15:35:00

那么,我终于找到了导致这个问题的原因,并且我发布了一个详细的答案,以防其他人有这些问题。

我在进程执行时尝试了jmap,但这通常会导致jvm进一步挂起,我将不得不使用--force运行它。这导致堆转储似乎缺少大量数据,或者至少错过了它们之间的引用。为了分析,我尝试了jhat,它提供了大量的数据,但没有太多解释它的方式。其次,我尝试了基于eclipse的内存分析工具(http://www.eclipse.org/mat/),该工具显示堆大部分是与tomcat相关的类。

问题在于jmap没有报告应用程序的实际状态,只能捕获关闭时的类,这主要是tomcat类。

我再次尝试了几次,并注意到模型对象的计数非常高(实际上比在数据库中标记为公共的多2-3倍)。

使用这个我分析了慢速查询日志和一些不相关的性能问题。我尝试了额外的延迟加载(http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html),以及用直接的jdbc查询替换了一些hibernate操作正在处理大型集合的加载和操作 - jdbc替换只是直接在连接表上工作),并替换了一些mysql正在记录的低效查询。

这些步骤改进了前端的性能,但仍未解决泄漏问题,该应用程序仍然不稳定并且行为不可预测。

最后,我找到了选项:-XX:+ HeapDumpOnOutOfMemoryError。这最终产生了一个非常大的(〜6.5GB)hprof文件,准确显示了应用程序的状态。具有讽刺意味的是,这个文件太大了,甚至在一个有16GB内存的盒子上也无法分辨。幸运的是,MAT能够产生一些漂亮的图形并显示出一些更好的数据。

这次突出的是一个石英线程占用了6GB堆的4.5GB,其中大部分是休眠状态StatefulPersistenceContext(https://www.hibernate.org/hib_docs/v3/api/org/hibernate /engine/StatefulPersistenceContext.html)。这个类在内部被hibernate用作它的主缓存(我已经禁用了EHCache支持的第二级和查询缓存)。

这个类用于启用大部分hibernate的功能,所以它不能直接禁用(你可以直接解决它,但是spring不支持无状态会话),如果这样做的话,我会很惊讶在成熟的产品中出现重大内存泄漏。那为什么现在漏水?

那么,这是一个事情的组合:石英线程池实例化某些东西是threadLocal,春天正在注入一个会话工厂,这是石英线程生命周期开始时创建一个会话,然后被重用来运行各种使用休眠会话的石英作业。然后Hibernate在会话中缓存,这是它的预期行为。

问题在于线程池永远不会释放会话,因此hibernate保持驻留并在会话的生命周期中维护缓存。由于这是使用spring hibernate模板支持,因此没有明确使用会话(我们使用的是dao - > manager - > driver - > quartz-job hierarchy,dao是通过spring注入休眠配置的,所以操作是直接在模板上完成)。

所以会话永远不会被关闭,hibernate保持对缓存对象的引用,所以它们永远不会被垃圾回收,所以每次运行一个新的作业时,它都会继续填充线程本地的缓存,所以甚至没有任何不同工作之间的分享。此外,由于这是一项写入密集型工作(很少阅读),缓存大部分都浪费了,所以对象不断创建。

解决方案:创建一个显式调用session.flush()和session.clear()的dao方法,并在每个作业开始时调用该方法。

该应用程序已运行了几天,无监视问题,内存错误或重新启动。

感谢大家在这方面的帮助,这是一个相当棘手的错误追踪,因为一切都完全按照它应该做的,但最终3线方法设法解决所有问题。

票数 0
EN

Stack Overflow用户

发布于 2018-03-01 16:40:18

您能在启用JMX的情况下运行生产盒吗?

代码语言:txt
复制
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=<port>
...

使用JMX进行监测和管理

然后附加JConsole,VisualVM

使用jmap进行堆转储可以吗?

如果是的话,你可以使用JProfiler(你已经拥有),jhat,VisualVM,Eclipse MAT来分析堆转储的泄漏。还比较可能有助于查找泄漏/模式的堆转储。

正如你所提到的雅加达 - 普通人。使用jakarta-commons-logging来保存类加载器时存在问题。为了更好地阅读该检查

记忆泄漏者生命中的一天(release(Classloader)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/-100004180

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档