我有一个应用程序,使用了相当多的演员:确切地说,25,000个。它使用Scala 2.7.7,并在jdk6_u18上运行。它基本上监听和处理市场数据,并且几乎没有状态。
它每天早上8:02启动,在一个小时内就会因为OutOfMemoryError而崩溃。 "啊哈"你说,"你有一个内存泄漏!"但是当我重新启动它时,它从来没有再在一天中的其他时间崩溃过!这尽管在美国市场在下午2:30开放时增加了GC和CPU开销。
一些轶事发现:
851.191: [GC pause (young) 228M->176M(256M), 0.0218611 secs]
851.414: [Full GC 254M->176M(256M), 1.9352357 secs]
853.492: [GC pause (young) 228M->176M(256M), 0.0224688 secs]
853.716: [Full GC 254M->176M(256M), 1.9339705 secs]
855.793: [GC pause (young) 228M->176M(256M), 0.0215707 secs]
856.009: [Full GC 254M->176M(256M), 1.9805797 secs]
858.137: [GC pause (young) 228M->176M(256M), 0.0223224 secs]
而且,很久以后仍然没问题!
25882.894: [GC暂停(年轻代) 从224M到125M(256M),用时0.2126515秒] 25884.880: [GC暂停(年轻代) 从224M到126M(256M),用时0.2059802秒] 25887.027: [GC暂停(年轻代) 从224M到125M(256M),用时0.1851359秒] 25889.940: [GC暂停(年轻代) 从223M到126M(256M),用时0.2046496秒] 25891.567: [GC暂停(年轻代) 从224M到126M(256M),用时0.1600574秒]
37182.163: [GC暂停(年轻代)(初始标记)225M->153M(256M)37182.326:[GC并发标记开始],0.1622246秒]
37183.089:[GC并发标记结束,0.7635219秒]
37183.090:[GC重新标记,0.0032547秒]
37183.093:[GC并发计数开始]
37183.297:[GC并发计数结束,0.2043307]
37183.393:[GC清理198M->198M(256M),0.0068127秒]
37183.400:[GC并发清理开始]
37183.400:[GC并发清理结束,0.0000393]
37183.648:[GC暂停(年轻代)222M->153M(256M),0.1483041秒]
37184.235:[GC暂停(部分)171M->91M(256M),0.2520714秒]
37187.223:[GC暂停(年轻代)221M->92M(256M),0.1721220秒]
它每天早上8:02启动,在一个小时内就会因为OutOfMemoryError而崩溃。 "啊哈"你说,"你有一个内存泄漏!"但是当我重新启动它时,它从来没有再在一天中的其他时间崩溃过!这尽管在美国市场在下午2:30开放时增加了GC和CPU开销。
一些轶事发现:
- 它在Solaris上运行。当我以前在Linux上运行它时,它根本没有崩溃。
- 我尝试过更改生成堆大小、分配更多内存等。我认为这没有任何区别。
- 当我打开
verbose:gc
时,收集器的行为似乎有所不同。
出现了一些问题:
- 为什么该程序在Linux和Solaris之间的表现会不同?
- 为什么在8.02和8.42启动程序时行为会不同?
- 我听说actors库存在一些内存泄漏问题。它们是什么,何时修复,我如何发现是否存在类似的问题?(在jhat中寻找的内容)
- 有人知道可能发生了什么吗?
我现在正在尝试使用G1
来查看是否有任何区别。我明天会更新这个问题!
G1输出的一些详细信息: 启用verbose:gc选项
我想我刚才抓住了它:
600.290: [Full GC 255M->144M(256M), 1.5772616 secs]
602.084: [GC pause (young) 227M->145M(256M), 0.0556769 secs]
602.418: [Full GC 254M->144M(256M), 1.6415216 secs]
604.279: [GC pause (young) 227M->145M(256M), 0.0415157 secs]
604.602: [Full GC 255M->145M(256M), 1.6041762 secs]
606.422: [GC pause (young) 227M->145M(256M), 0.0237441 secs]
606.710: [Full GC 254M->145M(256M), 1.6022185 secs]
然后稍后一点(你可以看到完全GC时间更长,回收的内存更少)
849.084: [Full GC 254M->176M(256M), 1.9658754 secs]851.191: [GC pause (young) 228M->176M(256M), 0.0218611 secs]
851.414: [Full GC 254M->176M(256M), 1.9352357 secs]
853.492: [GC pause (young) 228M->176M(256M), 0.0224688 secs]
853.716: [Full GC 254M->176M(256M), 1.9339705 secs]
855.793: [GC pause (young) 228M->176M(256M), 0.0215707 secs]
856.009: [Full GC 254M->176M(256M), 1.9805797 secs]
858.137: [GC pause (young) 228M->176M(256M), 0.0223224 secs]
使用verbose:gc输出的一些G1信息
又好了!*叹气*
303.656: [GC暂停(年轻代)225M->93M(256M),0.1680767秒] 308.060: [GC暂停(年轻代)226M->94M(256M),0.1793724秒] 312.746: [GC暂停(年轻代)227M->93M(256M),0.1674851秒] 316.162: [GC暂停(年轻代)227M->95M(256M),0.1826145秒] 320.147: [GC暂停(年轻代)226M->94M(256M),0.1656664秒] 325.978: [GC暂停(年轻代)226M->93M(256M),0.1475760秒] 330.176: [GC暂停(年轻代)226M->94M(256M),0.1727795秒]而且,很久以后仍然没问题!
25882.894: [GC暂停(年轻代) 从224M到125M(256M),用时0.2126515秒] 25884.880: [GC暂停(年轻代) 从224M到126M(256M),用时0.2059802秒] 25887.027: [GC暂停(年轻代) 从224M到125M(256M),用时0.1851359秒] 25889.940: [GC暂停(年轻代) 从223M到126M(256M),用时0.2046496秒] 25891.567: [GC暂停(年轻代) 从224M到126M(256M),用时0.1600574秒]
稍后还会进行一次完整的GC
37180.191: [GC暂停(年轻代)225M->154M(256M),0.1716404秒]37182.163: [GC暂停(年轻代)(初始标记)225M->153M(256M)37182.326:[GC并发标记开始],0.1622246秒]
37183.089:[GC并发标记结束,0.7635219秒]
37183.090:[GC重新标记,0.0032547秒]
37183.093:[GC并发计数开始]
37183.297:[GC并发计数结束,0.2043307]
37183.393:[GC清理198M->198M(256M),0.0068127秒]
37183.400:[GC并发清理开始]
37183.400:[GC并发清理结束,0.0000393]
37183.648:[GC暂停(年轻代)222M->153M(256M),0.1483041秒]
37184.235:[GC暂停(部分)171M->91M(256M),0.2520714秒]
37187.223:[GC暂停(年轻代)221M->92M(256M),0.1721220秒]
更新
自从在jdk1.6.0_18上切换到G1垃圾收集器后,该应用程序已经连续三天表现良好。我怀疑Erik关于VM在高吞吐量情况下将对象提升为老年代并陷入“死亡螺旋”的分析是正确的。
jhat
,我也无法分析我的256M堆! - oxbow_lakes