我正在测试一个用Java编写的API,它可以最小化处理通过网络接收的消息的延迟。为了实现这些目标,我尝试了可用的不同垃圾收集器。
我正在尝试四种不同的技术,它们利用以下标志来控制垃圾收集:
1)串行:-XX:+UseSerialGC
2)并行:-XX:+UseParallelOldGC
3)并发:-XX:+UseConcMarkSweepGC
4)并发/增量:-XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+CMSIncrementalPacing
我在五个小时的过程中运行了每一种技术。我定期使用ManagementFactory.getGarbageCollectorMXBeans()提供的GarbageCollectorMXBean列表来检索收集垃圾所用的总时间。
我的结果?请注意,这里的“延迟”是“我的application+the应用程序接口处理从网络上提取的每条消息所花费的时间”。
串行: 789个GC事件,总计1309ms;平均延迟47.45us,中位延迟8.704 us,最大延迟1197 us
并行: 1715个GC事件,总计122518 ms;平均延迟450.8 us,中位延迟8.448 us,最大延迟8292us
并发:4,629个GC事件,总计116229 ms;平均延迟707.2 us,中位延迟9.216 us,最大延迟9151us
增量: 5066个GC事件,总计200213 ms;平均延迟515.9 us,中位延迟9.472 us,最大延迟14209 us
我发现这些结果是如此不可能,以至于它们近乎荒谬。有人知道为什么我会有这样的结果吗?
哦,顺便说一句,我使用的是Java HotSpot(TM) 64位服务器VM。
发布于 2012-03-16 22:20:29
我正在开发一个Java应用程序,它可以最大化吞吐量和最小化延迟
这有两个问题:
的结果
您所显示的只是在垃圾收集器中花费了多少时间。如果您实际实现了更高的吞吐量,那么您可能希望看到在垃圾收集器中花费更多的时间。或者换一种说法,我可以在代码中进行更改,以使您报告的值非常容易最小化:
// Avoid generating any garbage
Thread.sleep(10000000);你需要弄清楚对你来说什么才是真正重要的。衡量所有重要的事情,然后找出权衡的地方。因此,首先要做的是重新运行测试并测量延迟和吞吐量。您可能还关心总的CPU使用率(当然,这与GC中的CPU不同),但是当您没有测量您的主要目标时,您的结果不会给您提供特别有用的信息。
发布于 2012-03-16 22:38:51
我一点也不觉得奇怪。
串行垃圾收集的问题是,当它运行时,其他任何东西都不能运行(也就是“停止世界”)。然而,这有一个好的观点:它将花费在垃圾收集上的工作量保持在最低限度。
几乎任何类型的并行或并发垃圾收集都必须做大量的额外工作,以确保对堆的所有修改对于其余代码都是原子的。它必须停止那些依赖于特定更改的事情,然后在足够长的时间内执行该特定更改,而不是仅仅停止一段时间。然后,它让该代码再次开始运行,转到下一个要进行更改的点,停止依赖它的其他代码片段,等等。
另一点(虽然在本例中可能是相当小的一点)是,随着您处理更多的数据,您通常希望生成更多的垃圾,因此花费更多的时间进行垃圾收集。由于串行收集器在执行其工作时会停止所有其他处理,因此这不仅可以加快垃圾收集速度,还可以防止在这段时间内生成更多垃圾。
现在,为什么我说在这种情况下这可能是一个次要的贡献者?这很简单:串行收集器在五个小时中只用了一秒钟多一点。尽管在大约1.3秒的时间内没有完成任何其他工作,但这只占5个小时的很小的百分比,可能对您的总体吞吐量没有太大的影响(如果有的话)。
摘要:串行垃圾收集的问题不在于它总体上占用了过多的时间--而在于当您碰巧需要快速响应时,如果它恰好停止运行,它可能会非常不方便。同时,我应该补充说,只要您的收集周期很短,这仍然可以是相当小的。理论上,其他形式的GC大多限制了最坏的情况,但实际上(例如,通过限制堆大小),您通常也可以限制串行收集器的最大延迟。
发布于 2012-03-16 22:40:34
在2012年的QCon Conference大会上,有一位推特工程师在这个话题上做了一次精彩的演讲--你可以在here上观看。
它讨论了Hotspot JVM内存和垃圾收集中的各种“代”(Eden、Survivor、Old)。特别要注意的是,ConcurrentMarkAndSweep中的“并发”只适用于老一代,即挂起一段时间的对象。
短暂的对象是来自“伊甸园”一代的GCd -这很便宜,但无论你选择哪种GC算法,它都是一个“停止世界”的GC事件!
建议首先调整年轻一代,例如分配大量的新伊甸园,这样就有更多的机会让对象在年轻时死亡并被廉价回收。使用+PrintGCDetails、+PrintHeapAtGC、+PrintTenuringDistribution...如果你的存活率超过100%,那么就没有空间了,所以物体很快就会升级为旧的--这是很糟糕的。
当调整老一代时,如果延迟是最重要的,建议先尝试自动调整的ParallelOld (+自适应大小策略等),然后尝试内容管理,然后可能是新的G1GC。
https://stackoverflow.com/questions/9738911
复制相似问题