我有an application which uses rather a lot of actors:准确地说是25,000。它使用Scala 2.7.7并在 jdk6_u18 上运行。它基本上监听和处理市场数据,并且状态很少。
它每天早上8.02开始,并在一个小时内以OutOfMemoryError
崩溃。您说“啊哈”,“您的内存泄漏了!”除非我重新启动它,否则永远不会崩溃在一天的剩余时间内再次崩溃!尽管在美国市场下午2:30开盘时,GC和CPU的开销都增加了,但这仍然没有问题。
一些轶事发现:
verbose:gc
时,收集器的行为似乎有所不同出现一些问题:
我现在正在尝试
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]
G1的一些输出,带有verbose:gc off
很好! *叹*
303.656: [GC pause (young) 225M->93M(256M), 0.1680767 secs]
308.060: [GC pause (young) 226M->94M(256M), 0.1793724 secs]
312.746: [GC pause (young) 227M->93M(256M), 0.1674851 secs]
316.162: [GC pause (young) 227M->95M(256M), 0.1826145 secs]
320.147: [GC pause (young) 226M->94M(256M), 0.1656664 secs]
325.978: [GC pause (young) 226M->93M(256M), 0.1475760 secs]
330.176: [GC pause (young) 226M->94M(256M), 0.1727795 secs]
而且很多以后它仍然可以!
25882.894: [GC pause (young) 224M->125M(256M), 0.2126515 secs]
25884.880: [GC pause (young) 224M->126M(256M), 0.2059802 secs]
25887.027: [GC pause (young) 224M->125M(256M), 0.1851359 secs]
25889.940: [GC pause (young) 223M->126M(256M), 0.2046496 secs]
25891.567: [GC pause (young) 224M->126M(256M), 0.1600574 secs]
再后来,一个完整的GC
37180.191: [GC pause (young) 225M->154M(256M), 0.1716404 secs]
37182.163: [GC pause (young) (initial-mark) 225M->153M(256M)37182.326: [GC concurrent-mark-start], 0.1622246 secs]
37183.089: [GC concurrent-mark-end, 0.7635219 sec]
37183.090: [GC remark, 0.0032547 secs]
37183.093: [GC concurrent-count-start]
37183.297: [GC concurrent-count-end, 0.2043307]
37183.393: [GC cleanup 198M->198M(256M), 0.0068127 secs]
37183.400: [GC concurrent-cleanup-start]
37183.400: [GC concurrent-cleanup-end, 0.0000393]
37183.648: [GC pause (young) 222M->153M(256M), 0.1483041 secs]
37184.235: [GC pause (partial) 171M->91M(256M), 0.2520714 secs]
37187.223: [GC pause (young) 221M->92M(256M), 0.1721220 secs]
更新
好吧,自从在jdk1.6.0_18上切换到G1垃圾收集器以来,该应用程序已经连续三天表现良好。我怀疑埃里克(Erik)对虚拟机在高吞吐量情况下陷入死亡螺旋的情况的分析是正确的,在这种情况下,虚拟机已将对象提升为终身制。
最佳答案
您是否有任何理由期望堆的大小逐渐增加?看起来在两条痕迹中它都在增长。我在许多情况下所做的一件事是减少堆,以使问题变得更糟。但是,256M大约是Scala的下限。
我以前注意到的一件事是,如果由于压力太大而使生命周期短的物体从伊甸园世代中脱颖而出,它可能会逐渐使您丧命。当事件量很大(也许您早上有时间吗?)并且您的伊甸园空间不够大时,可能会发生这种情况。总体来说,斯卡拉(Scala)尤其是 Actor 大量使用了小巧的小 object ,而且似乎有一个神奇的阈值,一旦您越过它,它就会全部下坡。因此,一次运行就可以了,下一次崩溃并烧毁。
我之前注意到的另一件事是,在OSX/x86上运行良好的GC设置在Sparc/Solaris上通常无法正常运行,反之亦然。如果您使用的是CoolThreads,建议您将GC配置为在调度程序/池中每个线程有一个GC线程。
这带来了另一件事-确保调度程序不会随意创建新线程。有时会那样做。我要说的是,几乎应该总是手动设置线程的上限。我不知道它的相关性如何,但是actor默认使用的关于fork-join调度程序的一个有趣的事实是,它旨在用于短期的,CPU限制的任务。在它管理的线程中执行IO会加重其假设。当然它应该仍然可以工作,但是...
祝你好运!我已经因为这类问题而失去了很多很多天。
看一下这里的一些选项:http://java.sun.com/performance/reference/whitepapers/tuning.html
看起来您正在使用并发标记清除收集器。尝试设置:
-XX:+UseParallelGC
关于scala - Scala actor应用程序的奇怪GC行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2183226/