与任何内存管理问题一样,这是一个很长的故事,所以很快。
我们的应用程序一直存在一些内存管理问题,所以我一直试图分析应用程序以了解问题所在。我今天早些时候看到了这个帖子:
Tomcat Session Eviction to Avoid OutOfMemoryError
......这似乎与我在探查器中看到的一样。基本上,如果我使用Jmeter的一堆用户点击应用程序,它会长时间保留在堆内存中,最终直到会话开始到期。然而,与该线程中的海报不同,我有源代码,并且可以选择尝试使用Tomcat实现持久状态会话,这是我今天尝试做的事情,但成效有限。我认为这是一些我缺少的配置设置。这是我在context.xml中的内容:
<Manager className='org.apache.catalina.session.PersistentManager'
saveOnRestart='false'
maxActiveSessions='5'
minIdelSwap='0'
maxIdleSwap='1'
maxInactiveInterval='600'
maxIdleBackup='0'>
<Store className='org.apache.catalina.session.FileStore'/>
</Manager>
在web.xml中我有这个:
<session-config>
<session-timeout>
10
</session-timeout>
</session-config>
理想情况下,我希望会话在一小时内超时,但出于测试目的,这很好。现在玩完了一些东西并最终进入这些设置后,我正在观看分析器中的应用程序,这就是我所看到的:
正如您在图片中看到的,我已经添加了一些注释。这是围绕问号圈出的部分,我真的不了解其中的任何事情。您可以看到我正在使用Jconsole&#39;执行GC&#39;按钮在几个不同的点,你可以看到图表中的部分,我正在使用Jmeter的许多客户端点击应用程序。
如果我没记错的话(我必须回去并确实记录下来),在我实现持久状态之前,GC按钮在清除堆时几乎不会那么多。这里奇怪的是,我似乎必须为这个持久状态手动运行GC以实际帮助任何事情。
或者,这只是一个普通的内存泄漏情况吗?今天早些时候,我进行了堆转储并将其加载到Eclipse Memory Analyzer工具中,并使用了“检测漏洞”。功能,它所做的一切都强化了这是一个会话规模问题的理论;它检测到的唯一泄漏是在java.util.concurrent.ConcurrentHashMap $ Segment中引导我进入这个线程Memory Fully utilized by Java ConcurrentHashMap (under Tomcat)
这让我觉得它并不是真正泄漏的应用。
其他相关细节: - 暂时在本地计算机上运行/测试。所有这些结果都来自于此。 - 使用Tomcat 6 - 这是一个JSF2.0应用程序 - 我根据Tomcat文档添加了系统属性-Dorg.apache.catalina.STRICT_SERVLET_COMPLIANCE = true
所以,我想这里有几个问题: 1.我是否正确配置了这个? 2.是否有内存泄漏? 3.此内存配置文件中发生了什么? 它(相对)正常吗?
提前致谢。
更新
所以,我尝试了肖恩的小贴士,并发现了一些新的有趣的东西。
会话侦听器运行良好,并且在分析此场景方面发挥了重要作用。还有一件事,我忘了提到的是应用程序的负载只是被单个页面混淆了,它在几乎可笑的比例上达到了功能复杂性。因此,在测试中,我有时会瞄准该页面,有时我会避开它。所以在我的下一轮测试中,这次使用会话监听器我发现了以下内容:
1)用几十个客户端点击应用程序,只需访问一个简单的页面。我注意到会话在指定的时间限制之后都按预期发布,并且内存已经释放。同样的事情与少数客户完美配合,对于复杂的情况,击中“大”的客户。页。
2)接下来,我尝试使用复杂的用例,使用几十个客户端来访问应用程序。这一次,发起的会议比预期多了几十个。似乎每个客户都在一到三个会话之间启动。在会话到期时间之后,释放了一点内存,但根据会话监听器,只有大约三分之一的会话被销毁。但是,与此相反,实际包含会话数据的文件夹是空的。大多数使用的内存也被保留。但是,在运行压力测试一小时后,垃圾收集器就会运行,一切都恢复正常。
因此,跟进问题包括:
1)为什么在简单的情况下可以正确处理会话,但是当事情变得紧张时,它们会被正确管理? Session处理程序是否误报,或者使用JMeter以某种方式影响它?
2)为什么垃圾收集器等待一小时才能运行?是否有系统设置要求垃圾收集器必须在给定的时间范围内运行,或者是否有某些配置设置丢失?
再次感谢您的继续支持。
更新2
只是一个简单的说明:再玩这个,我发现我得到不同的关于实时会话数量的报告的原因是由于我使用持久会话;如果我把它关闭,一切都按预期工作。它确实在Tomcat页面上表示持久会话,它是一个实验性功能。它必须是调用侦听器正在接收的会话事件,当人们非常期望它这样做时。
答案 0 :(得分:5)
让我尽力解决问题
对于您的配置,它在纸上看起来不错。如果要确保正确创建和销毁会话,则需要设置会话侦听器以记录或显示打开的会话列表。这是指向example
我认为没有内存泄漏。增量GC正在运行。似乎有很多记忆被提升到老一代。但是,一旦您的应用程序能够运行完整的GC,内存将恢复正常。如果您需要用户会话中存储大量数据,那么内存将是您的权衡。只要您在注销/超时后销毁数据,从长远来看内存应该没问题。 您的问题(???)区域将是您的会话仍处于活动状态的灰色区域,因此存储在其中的数据将被保留,此时可能会提升为旧的。一旦内存被提升为Old Gen,它需要一个完整的GC来清理它。单击按钮就可以了。该应用程序最终将安排完整的GC。
我认为#2有助于回答此问题
压力测试看起来很正常。尝试再次运行它而不按“执行GC”按钮。看看内存是否会自行退回。请注意,当垃圾收集器认为应该执行此操作时,它将执行完整的GC。要清除旧代,需要暂停,以便GC不会执行该操作,除非它感觉没有足够的资源来正确管理应用程序。
您可能希望为将来的测试做一些其他事情。记下年轻一代和旧一代堆之间的堆使用情况。从屏幕截图中我们可以看到有872mb的堆已提交,因此在负载测试期间分解内存的位置将有助于正确识别堆的保存位置。
以类似的方式考虑启用verbose GC日志记录,以便您可以获得堆分解的报告。有很多工具可以帮助您绘制GC日志图,这样您就不必手动读取日志。如果您不希望将其发送到stdout,也可以将其记录到文件(-Xloggc:file)。
如果会话大小变得太大而无法管理,您可能希望将该内存卸载到其他位置。一些示例可能是使用BigMemory,DB Persistance或其他一些减少会话大小的方法。
答案 1 :(得分:0)
我没有看到有人指出你的错字:
minIdelSwap='0'
不确定这对任何人都有用,但肯定会导致PersistenceManager的行为与你预期的不同。