一步步优化JVM五:优化延迟或者响应时间(2)
优化CMS(concurrent garbage collection) 使用CMS,old代的垃圾回收执行线程会和应用程序的线程最大程度的并发执行。这个提供了一个机会来减少最坏延迟的频率和最坏延迟的时间消耗。CMS没有执行压缩,所以可以避免old代空间的stop-the-world压缩(会让整个应用暂停运行)。
优化CMS的目标就是避开stop-the-world压缩垃圾回收,然而,这个说比做起来容易。在一些的部署情况下,这个是不可避免的,尤其是当内存分配受限的时候。
在一些特殊的情况下,CMS比其他类型的垃圾回收需要更多优化,更需要优化young代的空间,以及潜在的优化该什么时候初始化old代的垃圾回收循环。
当从吞吐量垃圾回收器(Throughput)迁移到CMS的时候,有可能会获得更慢的MinorGC,由于对象从young代转移到old会更慢 ,由于CMS在old代里面分配的内存是一个不连续的列表,相反,吞吐量垃圾回收器只是在本地线程的分配缓存里面指定一个指针。另外,由于old代的垃圾回收线程和应用的线程是尽可能的并发运行的,所以吞吐量会更小一些。然而,最坏的延迟的频率会少很多,由于在old代的不可获取的对象能够在应用运行的过程被垃圾回收,这样可以避免old代的空间溢出。
使用CMS,如果old代能够使用的空间有限,单线程的stop-the-world压缩垃圾回收会执行。这种情况下,FullGC的时间会比吞吐量垃圾回收器的FullGC时间还要长,导致的结果是,CMS的绝对最差延迟会比吞吐量垃圾回收器的最差延迟严重很多。old代的空间溢出以及运行了stop-the-world垃圾回收必须被应用负责人重视,由于在响应上会有更长的中断。因此,不要让old代运行得溢出就非常重要了。对于从吞吐量垃圾回收器迁移到CMS的一个比较重要的建议就是提升old代20%到30%的容量。
在优化CMS的时候有几个注意点,首先,对象从young代转移到old代的转移率。其次,CMS重新分配内存的概率。再次,CMS回收对象时候产生的old代的分隔,这个会在可获得的对象中间产生一些空隙,从而导致了分隔空间。
碎片可以被下面的几种方法寻址。第一办法是压缩old代,压缩old代空间是通过stop-the-world垃圾回收压缩完成的,就像前面所说的那样,stop-the-world垃圾回收会执行很长时间,会严重影响应用的响应时间,应该避开。第二种办法是,对碎片编址,提高old代的空间,这个办法不能完全解决碎片的问题的,但是可以延迟old代压缩的时间。通常来讲,old代越多内存,由于碎片导致需要执行的压缩的时间久越长。努力把old的空间增大的目标是在应用的生命周期中,避免堆碎片导致stop-the-world压缩垃圾回收,换句话说,应用GC最大内存原则。另外一种处理碎片的办法是减少对象从young代移动到old的概率,就是减少MinorGC,应用MinorGC回收原则。
任期阀值(tenuring threshold)控制了对象该什么时候从young代移动到old代。任期阀值会在后面详细的介绍,它是HotSpot VM基于young代的占用空间来计算的,尤其是survivor(幸存者)空间的占用量。下面详细介绍一下survivor空间以及讨论任期阀值。
survivor空间
survivor空间是young代的一部分,如下图所示。young代被分成了一个eden区域和两个survivor空间。
两个survivor空间的中一个被标记为“from”,另外一个标记为“to”。新的Java对象被分配到Eden空间。比如说,下面的一条语句:
-Xmx1568m -Xms1658m -Xmn544m -XX:SurvivorRatio=15 -XX:MaxTenuringThreshold=3