我正在编写我的servlet程序并使用jconsole和jmap来监视它的内存状态。我发现当我的程序运行时,Memory Pool" PS Old Gen"变得越来越大,最后我的servlet无法响应任何请求。
这是我的 JConsole 输出的截图:
当我点击"执行GC"按钮,什么也没发生。
因此,要查看详细信息,我使用jmap转储详细信息:
这是我的JConsole VM Summary输出: 任何人都可以帮我解决可能出现的问题吗?你知道吗,GC" PS MarkSweep"和" PS SCavenge"是我的服务器JVM的默认GC。
谢谢。
我发现了一个非常奇怪的现象:在从昨天的18:00到今天的09:00的15个小时内,似乎GC在" PS Old Gen"从未发生过,这使得老一代的旧内存变得越来越大。我只需要手动点击"执行GC"按钮,似乎这个GC非常有效并且回收了大量内存。但是为什么老一代GC没有自动发生这么长时间?我们可以看到,在昨天的18:00之前,旧一代GC正常工作。
答案 0 :(得分:1)
您的内存泄漏发生在MongoDB代码中。您看到的大量地图条目很可能是BasicDBObject
(转储中排名第6)的内部,其扩展为HashMap
。您可以通过重新配置MongoDB组件来解决问题。
答案 1 :(得分:1)
假设您没有添加选项-histo:live当您使用jmap时,这将导致使用垃圾+活动对象获取报告,并引用手动单击&#34时发生的内存丢失;执行GC"按钮,我怀疑应用程序没有内存泄漏,但是从Young Gen到Old Gen的对象促销率不佳。最终,旧Gen将被填满并将运行完整的GC导致应用程序无响应。
如果我的假设是正确的,我认为你的策略应该是尽量减少将对象推广到旧代。而不是担心要做什么来清除更昂贵的旧Gen。参考您提到的以下评论,我认为您的应用程序相对于最大分配内存7G具有较小的内存占用(< 0.5G)。
"我所有的数据密集型变量都是在方法中定义的。返回方法时,应回收这些变量,对吗?"
所以你可以做的事情很少。
调整应用程序以最小化事务的响应时间,以便在提升为旧代之前对对象进行垃圾回收
增加Young Gen的大小。由于你有大约7 GB可以使用,为什么不为Young Gen分配大约2 - 3 GB(即-XX:NewSize = 2g)。较大的新尺寸将降低PCSacavenge(Young Collections)的频率,并将降低活动对象的老化速度。
然后开始调整-XX:MaxTenuringThreshold = n。你可以使用-XX:+ PrintTenuringDistribution我们使用gc.log。确定幸存者比例-XX:SurvivorRatio = n。请注意,默认情况下-XX:+ UseAdaptiveSizePolicy处于启用状态,这将动态更改Survivor Ratios的初始大小。否则,您可以跳过调整Survivor Ratios的大小,让AdaptiveSizePolicy完成这项工作。但我不是AdaptiveSizePolicy的忠实粉丝。
除了AdaptiveSizePolicy,您还可以使用-XX:MaxGCPauseMillis = n,以便在清除旧Gen时向垃圾收集器显示您在应用程序中预期的暂停。这样收集器将尝试实现MaxGCPauseMillis不要等到有太多工作要做。
或者你可以切换到CMS收集器,它是为处理这类响应时间问题而构建的。
嗯,我认为如果前两个步骤可以解决您的问题,那么您可以将其余部分放在一边。你不能通过添加一些额外的东西破坏运行良好的应用程序。重要的是你必须逐步调整GC。