首页 > 代码库 > 也学习Java/JVM/GC (三)
也学习Java/JVM/GC (三)
GC算法
目前HotSpot的GC算法是针对分代的GC算法,主要包括串行GC、并行GC、CMS GC和G1。
一、串行GC
三、CMS GC
全称The Concurrent Mark Sweep (CMS) Collector。CMS是年老代垃圾收集器。CMS试着最小化由并发垃圾收集工作造成的暂停时间。通常情况下,并发低暂停的收集器不会复制或压缩活着的对象。一次垃圾收集不会移动活着的对象。如果有碎片的问题,分配更大的堆。
CMS收集器用来在有低暂停和垃圾收集器共享资源的应用。包含桌面UI响应事件的程序,web服务器响应请求的程序等。
用法:
参数为:-XX:+UseConcMarkSweepGC,设置回收线程数量为:-XX:+ParallelCMSThreads=<desired number>
例如:java -Xms20m -Xmx20m -Xmn10m -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails JvmTest
四、G1
并发标记清理(CMS)收集器(也可以成为并发低暂停收集器)回收年老代内存。CMS的大部分工作和应用程序并发执行,以此来减少由垃圾收集造成的暂停时间。通常情况下并发低暂停垃圾收集器不会复制和压缩活的对象。垃圾回收是在不移动活着的对象完成的。如果有碎片问题,可以分配一个更大的堆。
注意:CMS垃圾收集器在年轻代使用和并行回收收集器相同的算法。
CMS 收集阶段
CMS收集器在年老代中分为以下几个阶段执行
1、初始标记(STW):包括来自年轻代中可达在内的所有年老代的对象根据这些对象的可达情况被标记。相对与MGC的暂停时间通常是短暂的。
2、并发标记:当java程序正在执行时并发的遍历年老代的可达对象图。扫描从被标记的对象开始,直到遍历所有由根部可达的对象。变更器(mutators)在并发的2、3和5阶段被执行,在CMS回收阶段分配内存的对象被立即标记为存活状态。
3、再次标记(STWE):找到在并发标记阶段(第2步)遗漏的对象,这些遗漏的对象是由于在并发标记后应用线程更新的。
4、并发清理:收集在标记阶段被识别为不可达的对象。收集的死对象空间加入到空闲列表,为后续分配内存使用。死对象的合并也可能在此时发生。注意活的对象不会被移动。
5、重置:清理数据结构为下一次并发收集做准备。
年青代是被浅绿色标记,年老代被蓝色标记。如果你的应用已经运行了一段时间看起来就会像图中一样。对象散落在年老代的各个地方。
使用CMS,年老代对象被就地回收。它们不会被移动到其它地方。除fgc外年老代不会被压缩
3、年青代的垃圾收集在eden区和survivor区存活的对象被移动到另外一个survivor区。任何对象达到了最大年龄阀值就会被放到年老代。
两侧STW发生时间:初始标记和再标记。当年老代达到了一定的容量比例时,CMS开始执行。
(1)初始标记是一个短暂停,活着的(可达的)对象会被标记。(2)当应用程序运行期间并发标记寻找活着的对象。最后(3)再标记阶段,找到在阶段(2)中被错过的对象。
6、年老代回收,并发清理在第(4)步清理阶段之后,你可以看到许多空闲的内存。也可以看到空间没有进行压缩。
最后,CMS收集器会进行第(5)重置阶段并且等待下次GC阀值到达。
G1收集器采用了一种不同的方式分配内存。下面的图例分布描绘了G1系统。
G1的堆是一快被分成许多固定大小的内存区域。
G1堆的大小是在JVM启动的时候设定的。JVM通常设置2000个区,区的大小范围为从1Mb到32Mb。
实际上,这些区域和逻辑上的eden, survivor, old generation space相当。
上图中各区域的颜色代表了相应的角色。活的对象被从一个区域转移(复制和移动)到另外一个区域。各个区被设计为并行收集,并且不会停止所有应用线程。
在图中展示的区域被分配为Eden, survivor和old generation 区。另外,还有第4种类型的区域被成为巨无霸区域。这些巨无霸区域被设计用来保存超过标准区50%或更大的对象。它们被存储在一款连续的区域。最后一种类型的区域是堆中未使用的区域。
3、G1中的年青代
堆大约被划分为2000个区域。最小的堆为1Mb,最大的堆为32Mb。蓝色的区域保存年老代对象,绿色的区域保存年青代的对象。
注意G1中的区不需要像老的垃圾收集器一样是连续的。
4、G1的的ygc活的对象被转移到一个或多个survivors区中。如果对象的收集次数达到阀值,对象会被晋升的年老代。
这会发生一次STW暂停。Eden 大小和 survivor的大小会被计算为下次ygc的时候使用。计数信息会保持用来帮助计算Eden和survivor的大小。像暂停时间这类的的事情也会被考虑。
这个方法使重新设置区的大小更加容易,让其更大或更小来满足需求。
5、G1的一次年青代GC后
存活的对象已经被转移到survivor区活着年老代了。
最近晋升的对象用深蓝色展示。Survivior区域用绿色展示。
总之,年青代的G1可以描述如下:
堆是被分成许多区域的单独内存空间。
年青代的内存是由不连续的区组成的。使得当需要变更这些区的大小时更加容易。
年青代的垃圾回收活ygc,是STW 事件。所有的应用线程被停止。
ygc由多线程并行执行。
存活的对象被转移到新的survivor活年老代。
年老代用G1进行垃圾收集
同CMS 收集器一样,G1收集器也被设计为一款在年老代的低暂停的垃圾收集器。下表描述了年老代的G1来及收集情况。
G1收集阶段 - 并发标记周期阶段
G1收集器在堆中的年老代中进行如下阶段的垃圾收集。注意有一些阶段也是年青代收集的一部分。
阶段 | 描述 |
(1)初始标记(STW) | 这会发生STW事件。使用G1,这是一次正常的年轻代GC的责任。标记这些可能引用到年老代对象的survivor区域(根区域)。 |
(2)扫描根区域 | 扫描有引用年老代的survivor区域。这个阶段应用程序可以继续运行。这个阶段必须在一次ygc发生前完成 |
(3)并发标记 | 找到整个堆存活的对象。这个阶段应用程序是正在运行的。这个阶段会被年青代的垃圾收集中断。 |
(4)再标记(STW) | 在堆中标记完成存活的对象。使用一种名为在开始时快照(SATB)的算法,这个算法比用CMS收集器的算法更快。 |
(5)清理(触发STW 事件并且并发执行) | 在存活的对象和完整空闲堆上进行统计(STW)。 清理Remembered Sets(STW)。 重新设置空区域并且返回这些区域的空闲列表(并发执行)。 |
(6)拷贝(触发STW Event) | 暂停应用转移和复制存活的对象到新的未使用的区域。这些操作在年青代中执行被日志记录为[GC pause (yong)]。或者同时在年青代和年老代执行,日志记录为[GC Pause (mixed)]。 |
G1的年老代垃圾收集步骤
根据G1的各个阶段定义,让我们看看G1收集器如何和年老代进行交互
6、初始标记阶段初始标记阶段是年青代垃圾收集的责任。在日志中的格式如下 (young)(inital-mark)
7、并发标记阶段
在再标记阶段如果有空的区域被发现(标记为红×的区域),这些区域会立即被删除。同样,“统计”信息决定了存活对象的计算。
8、再标记阶段
空的区域被删除和回收利用。现在计算所有区的活跃度。
9、复制/清理阶段
G1选择最低活跃度的区域,这些区域可以被快速的收集。然后这些区域和ygc的同时被收集。这个阶段在日志中用 [GC parse (mixed)]表示。因此年青代和年老代是在相同时间被回收的。
10、复制/清理阶段后
被选择收集和压缩的区域用深蓝色和深绿色表示。
年老代GC总结
总之,G1对年老代的GC有如下几个关键点
并发表级阶段
活跃度信息实在应用程序运行时并发运行的。
在转移暂停阶段活跃度信息是标识哪个区域会是最好的回收利用区域。
不像CMS有清理阶段。
再标记阶段
使用比在CMS中更加快速的开始快照算法。
完整的区域是会被重新利用的。
复制/清理阶段
年青代和年老代同时被回收。
年老代是基于活跃度选择的。
也学习Java/JVM/GC (三)