JVM调优总结(九)-新一代的垃圾回收算法

垃圾回收的瓶颈
    传统分代垃圾回收方式,已经在必定程度上把垃圾回收给应用带来的负担降到了最小,把应用的吞吐量推到了一个极限。可是他
没法解决的一个问题,就是Full GC所带来的应用暂停。在一些对实时性要求很高的应用场景下,GC暂停所带来的请求堆积和请求失
败是没法接受的。这类应用可能要求请求的返回时间在几百甚至几十毫秒之内,若是分代垃圾回收方式要达到这个指标,只能把最大
堆的设置限制在一个相对较小范围内,可是这样有限制了应用自己的处理能力,一样也是不可接收的。
    分代垃圾回收方式确实也考虑了实时性要求而提供了并发回收器,支持最大暂停时间的设置,可是受限于分代垃圾回收的内存划
分模型,其效果也不是很理想。
    为了达到实时性的要求(其实Java语言最初的设计也是在嵌入式系统上的),一种新垃圾回收方式呼之欲出,它既支持短的暂停
时间,又支持大的内存空间分配。能够很好的解决传统分代方式带来的问题。
 
 
增量收集的演进
    增量收集的方式在理论上能够解决传统分代方式带来的问题。增量收集把对堆空间划分红一系列内存块,使用时,先使用其中一
部分(不会所有用完),垃圾收集时把以前用掉的部分中的存活对象再放到后面没有用的空间中,这样能够实现一直边使用边收集的
效果,避免了传统分代方式整个使用完了再暂停的回收的状况。
    固然,传统分代收集方式也提供了并发收集,可是他有一个很致命的地方,就是把整个堆作为一个内存块,这样一方面会形成碎
片(没法压缩),另外一方面他的每次收集都是对整个堆的收集,没法进行选择,在暂停时间的控制上仍是很弱。而增量方式,经过内
存空间的分块,偏偏能够解决上面问题。
 
 
Garbage Firest(G1)
这部分的内容主要参考这里,这篇文章算是对G1算法论文的解读。我也没加什么东西了。
 
 
目标
从设计目标看G1彻底是为了大型应用而准备的。
支持很大的堆
高吞吐量
  --支持多CPU和垃圾回收线程
  --在主线程暂停的状况下,使用并行收集
  --在主线程运行的状况下,使用并发收集
实时目标:可配置在N毫秒内最多只占用M毫秒的时间进行垃圾回收
固然G1要达到实时性的要求,相对传统的分代回收算法,在性能上会有一些损失。
 
 
算法详解

    G1可谓博采众家之长,力求到达一种完美。他吸收了增量收集优势,把整个堆划分为一个一个等大小的区域(region)。内存
的回收和划分都以region为单位;同时,他也吸收了CMS的特色,把这个垃圾回收过程分为几个阶段,分散一个垃圾回收过程;并且
,G1也认同分代垃圾回收的思想,认为不一样对象的生命周期不一样,能够采起不一样收集方式,所以,它也支持分代的垃圾回收。为了达
到对回收时间的可预计性,G1在扫描了region之后,对其中的活跃对象的大小进行排序,首先会收集那些活跃对象小的region,以
便快速回收空间(要复制的活跃对象少了),由于活跃对象小,里面能够认为多数都是垃圾,因此这种方式被称为Garbage First(G1)的垃圾回收算法,即:垃圾优先的回收。
 
 
回收步骤:
 
初始标记(Initial Marking)
    G1对于每一个region都保存了两个标识用的bitmap,一个为previous marking bitmap,一个为next marking bitmap,bitmap中包含了一个bit的地址信息来指向对象的起始点。
    开始Initial Marking以前,首先并发的清空next marking bitmap,而后中止全部应用线程,并扫描标识出每一个region中
root可直接访问到的对象,将region中top的值放入next top at mark start(TAMS)中,以后恢复全部应用线程。
    触发这个步骤执行的条件为:
    G1定义了一个JVM Heap大小的百分比的阀值,称为h,另外还有一个H,H的值为(1-h)*Heap Size,目前这个h的值是固定的,
后续G1也许会将其改成动态的,根据jvm的运行状况来动态的调整,在分代方式下,G1还定义了一个u以及soft limit,soft limit
的值为H-u*Heap Size,当Heap中使用的内存超过了soft limit值时,就会在一次clean up执行完毕后在应用容许的GC暂停时间范
围内尽快的执行此步骤;
    在pure方式下,G1将marking与clean up组成一个环,以便clean up能充分的使用marking的信息,当clean up开始回收时
,首先回收可以带来最多内存空间的regions,当通过屡次的clean up,回收到没多少空间的regions时,G1从新初始化一个新的
marking与clean up构成的环。
 
并发标记(Concurrent Marking)
    按照以前Initial Marking扫描到的对象进行遍历,以识别这些对象的下层对象的活跃状态,对于在此期间应用线程并发修改的
对象的以来关系则记录到remembered set logs中,新建立的对象则放入比top值更高的地址区间中,这些新建立的对象默认状态即为
活跃的,同时修改top值。
 
 
最终标记暂停(Final Marking Pause)
    当应用线程的remembered set logs未满时,是不会放入filled RS buffers中的,在这样的状况下,这些remebered set 
logs中记录的card的修改就会被更新了,所以须要这一步,这一步要作的就是把应用线程中存在的remembered set logs的内容进行
处理,并相应的修改remembered sets,这一步须要暂停应用,并行的运行。
 
 
存活对象计算及清除(Live Data Counting and Cleanup)
    值得注意的是,在G1中,并非说Final Marking Pause执行完了,就确定执行Cleanup这步的,因为这步须要暂停应用,G1
为了可以达到准实时的要求,须要根据用户指定的最大的GC形成的暂停时间来合理的规划何时执行Cleanup,另外还有几种状况
也是会触发这个步骤的执行的:
    G1采用的是复制方法来进行收集,必须保证每次的”to space”的空间都是够的,所以G1采起的策略是当已经使用的内存空间达
到了H时,就执行Cleanup这个步骤;
    对于full-young和partially-young的分代模式的G1而言,则还有状况会触发Cleanup的执行,full-young模式下,G1根据
应用可接受的暂停时间、回收young regions须要消耗的时间来估算出一个yound regions的数量值,当JVM中分配对象的young 
regions的数量达到此值时,Cleanup就会执行;partially-young模式下,则会尽可能频繁的在应用可接受的暂停时间范围内执行
Cleanup,并最大限度的去执行non-young regions的Cleanup。
相关文章
相关标签/搜索