第三章(二)GC

2018-07-07  本文已影响9人  靈08_1024

本篇主要讲 垃圾收集算法HotSpot的的算法实现垃圾收集器

垃圾收集算法

标记-清除算法

Mark-Sweep,最基础的算法。


image.png

过程:(2阶段)
阶段一:标记要回收的对象;
阶段二:清除被标记的对象。

优点:

缺点:

复制算法

Copying,将内存分为均等的两块,每次只使用一块。解决了标记-清除的效率问题。


image.png

图中保留区域是另一半未使用的内存。

过程:(2阶段)
(前置:将内存均等为两块:内存块A 和 内存块B)
阶段一:复制存活的对象从内存块A到内存块B;
阶段二:清除内存块A。

优点:

缺点:

标记-整理算法

Mark-Compact,在标记-清除算法上添加整理功能。


image.png

过程:(3阶段)
阶段一:标记要回收的对象;
阶段二:清除被标记的对象;
阶段三:整理存活对象至一端,清理端边界意外的内存。

优点:

缺点:

分代收集算法

根据内存中对象的生存周期来按块回收。
在Java中,堆分为新生代老年代
在新生代中每次都有大量的对象死去,只有少量存活,采用 复制算法
在老年代中对象的存活率高,没有额外空间进行分配担保,就必须使用标记-清理标记-整理算法进行回收。

在HotSpot中,新生代分为1个Eden空间、2个Survivor空间,默认Eden和Survivor大小比为8:1。每次使用Eden和其中一个Survivor,当回收时,将Eden和该Survivor中存活的对象一次性复制到另一块Survivor空间,然后清理Eden和刚才用过的Survivor。当Survivor空间不够时,老年代参与分配担保机制。

HotSpot的的算法实现

HotSpot的算法实现,依赖于以下几点技术:

                         OopMap扩展
Java底层是以字节指令来执行的,但又不能每条指令都生成OopMap,太耗空间。在HotSpot的实现中只有在安全点时才会生成OopMap。
OopMap数据结构:OopMap{ebx=Oop [16]=Oop off=142}
指明了EBX寄存器和栈中偏移量为16的内存区域各有一个普通对象指针(Ordinary Object Pointer)的引用。
off是OopMap记录的偏移量,在本地码中,mov指令+off=hlt指令。
抢先式中断,在GC时,让所有线程都中断,如果有没有中断到安全点上的,恢复并使其“跑”到安全点上。现在几乎没有虚拟机采用这种方式。
主动式中断,在GC时,设置一标志,各个线程轮询这个标志,发现中断标志为真则中断自己。轮询标志和安全点是重合的。

垃圾收集器

这段讲述Serial收集器ParNew收集器Parallel Scavenge收集器Serial Old收集器Parallel Old收集器CMS收集器G1收集器的说明、使用场景。

Serial ParNew Parallel Scavenge Serial Old Parallel Old CMS G1
单/多线程
并行/并发 - 并行 并行 - 并行 并发 并发
Client/Server Client Server Server 主C副S Server Server Server
所属年代 新生代 新生代 新生代 老年代 老年代 老年代 所有
算法 - - 复制 - 复制 标记-清除 整体标记-整理,局部复制

上面的表格是各个收集器的一些通用属性的比较,下面是对各个收集器的一些补充,包括其主要配置参数。
所有的单线程收集器都是串行的。
所有的新生代都是采用复制算法。

Serial收集器

这是一个最基本、历史悠久的收集器。他会暂停其他所有工作线程,来执行它的垃圾收集,直到收集结束才会恢复其他线程。所以改收集器的别名:串行收集器。
Serial收集器由于没有线程交互开销,可以专心的垃圾回收,自然其可以获得最高的单线程收集效益。而在用户桌面应用场景中,分配给JVM的内存不会很大。Serial收集器收集两百兆以内的新生代,Stop The World(以下简称STW)停顿时间可以控制在几十毫秒左右,而这也是客户可以接受的。

ParNew收集器

相关参数:
-XX:ParallelGCThreads:限制垃圾收集的线程数。

ParNew收集器在单线程环境中绝对不比Serial收集器工作得好。由于其存在线程交互的开销,该收集器通过超线程技术实现的两个CPU的环境中都不能100%保证超过Serial收集器。但其对资源使用率有效充分。

Tips:超线程技术,CPU可以同时执行多个线程的一种技术,提升了资源使用率,但CPU的性能很难有突破。

Parallel Scavenge收集器

这是一个以达到一个可控制的吞吐量为目的的收集器。也叫吞吐量优先收集器

相关参数:
-XX:MaxGCPauseMills:设置最大垃圾的停顿时间(毫秒数)。x>0。
-XX:GCTimeRatio:设置GC时间比率。100>x>0。该值的意义是最多花费(1/(1+x))来收集最大1%的垃圾。如x=19,即最多花费5%的时间来收集1%的垃圾。其中1%是默认值。默认的垃圾值为99,1/(1+99)=1%。
-XX:+UseAdaptiveSizePolicy:自动调配新生代参数(如大小、Eden:Survivor比例、晋升老年代年龄等)。

吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),在吞吐量中,垃圾收集时间就是停顿时间,而GC的停顿时间太短,会导致GC的频繁,这样吞吐量也就降低了。
该收集器追求的高吞吐量,适用于后台运算(运算与CPU有关,这样可以充分利用CPU)而不需要太多交互的任务。

Serial Old收集器

Serial的老年代版本。

使用场景
主要给Client模式下的虚拟机使用。
在Server端,一是给JDK1.5及之前的Parallel Scavenge收集器搭配;一是CMS收集器的后备方案(见CMS)。

Parallel Old收集器

Parallel Scavenge的老年代版本。

CMS收集器

Concurrent Mark Sweep,并发标记清除收集器。一种以获取最短回收停顿时间为目标的收集器。from JDK1.5。

过程:

优点:

缺点:

相关参数
-XX:CMSInitiatingOccupancyFraction:CMS初始化容量百分比。
若CMS运行期间预留内存不够程序所需,会出现“Concurrent Mode Failure”失败,此时JVM会启动备案:临时启动Serial Old收集器来收集,而此时的停顿时间就更长了。
-XX:+UseCMSCompactAtFullCollection:在CMS进行Full GC时进行碎片整合。默认开启。内存整理过程无法并发,所以时间变长。
-XX:CMSFullGCsBeforeCompaction:执行n次不压缩的Full GC后,跟着来一次带压缩的。(默认为0,即每次Full GC都会整理)

G1收集器

Garbage-First收集器。

优点:

G1的堆结构
使用G1时,Java堆结构有别于其他收集器。它将整个Java堆分为多个大小相等独立的Region,虽然还有新生代、老年代的概念,但他们不再物理隔离,都是一部分Region(不需要连续)的集合。

G1的停顿时间模型
G1之所以可以建立停顿时间模型,因为G1可以有计划的避免全区垃圾扫描。G1跟踪各个Region里的垃圾堆价值大小(回收获得的空间大小以及回收所需时间的经验值),在后台维护一个列表,每次根据允许的收集时间,优先回收价值最大的Region(这是Garbage-First名字的由来)。这种按区划分空间以及按优先级回收区域的方式,保证了G1在优先的时间内可以获得更高的收集效率。

G1的化整为零
G1的内存思路是“化整为零”。G1中的所有Region都不是孤立的。在G1中所有的Region都有一个Remembered Set与之关联,在对Reference类型数据进行写操作时,会暂停中断并检查Reference引用的对象所属Region,并写到自己Region的Remembered Set中。当进行回收时,在GC Roots的枚举范围内加入Remembered Set来避免全堆扫描。

过程:(不含维护Remembered Set)

设置:通过配置-XX:+UseG1GC参数来使用。
当应用中存活的对象数量突然发生变化的时候,会对gc的暂停时间有很大影响。我们可以通过-XX:G1MaxNewSizePercent参数降低最大年轻代的大小,从而减少暂停期间最大需要回收的年轻代对象。

联想:G1的最终标记中Logs到Set的做法,类似的还有ES的trancingLog到segment,Redis的aof到内存。

GC日志查看

//TODO:以后查看日志了,复制一段,然后解说

上一篇 下一篇

猜你喜欢

热点阅读