Java教程

JVM的各种垃圾收集器

本文主要是介绍JVM的各种垃圾收集器,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

新生代

Serial收集器 (复制算法)

新生代收集器,Serial英文连续串行,这个收集器是单线程的收集器。它的“单线程”不仅仅是只会使用一个CPU或者一条线程完成垃圾收集工作,更重要的是它在进行垃圾收集时,其他工作线程必须暂停,直至收集结束。优点就是简单高效,对于只有单个CPU的环境来说,没有线程交互的开销,专心做垃圾收集效率最好。缺点时收集时其他线程需要暂停,如果暂停时间不长还可以接受,太长的话影响程序的效率,而且如果有多个CPU的话只使用一个就浪费资源,速度慢。

ParNew收集器(复制算法)

新生代收集器,ParNew其实就是Serial收集器的多线程版本。除了采用多线程之外,其他的和Serial完全一样。Parnew在单CPU的环境下并没有Serial的效率好,但是在多个CPU的情况下对GC资源的有效利用还是有好处的。它默认开启和CPU数量相同的线程数,速度会比单线程高很多。

Parallel Scavenge收集器(复制算法)

新生代收集器,Parallel Scavenge 也是并行的多线程收集器,它的目标时达到一个可控的吞吐量。所谓吞吐量就是CPU运行用户代码的时间与CPU总消耗的时间比值,即吞吐量 = 运行用户代码时间 / (运行用户代码时间+ 垃圾收集时间)。垃圾收集停顿时间越短(其他收集器追求的),越能提高用户的体验,而高吞吐量(Parallel 追求的)则可以高效率的利用CPU时间,尽快的完成程序的运算任务,主要适合后台运算量大而且不需要太多交互的任务。

老年代

Serial Old收集器(标记-整理算法)

serial收集器的老年代版本,也是单线程的收集器。主要有两大用途:
1.与新生代收集器Parallel Scavenge 配合使用
2.作为CMS收集器的后背预案,也就是充当保底角色,当CMS为了追求更高的效率而崩溃的时候,拿Serial Old顶上。

Parallel Old 收集器(标记-整理算法)

Parallel Scavenge的老年代版本,同样是多线程收集器。也是追求吞吐量的,主要是和新生代的Parallel Scavenge搭配。这两个搭配都是注重吞吐量的,最后的整体GC的吞吐量效果才比较好,否则新生代追求吞吐量,但是老年代追求停顿时间短,那最后整体的结果就是四不像,效果肯定不好。

CMS收集器(标记-清除算法)

CMS(Concurrent Mark Sweep 并发标记清除)以获取最短的停顿回收时间为目标。从名字也可以看出是采用标记-清除算法,整体过程分为4个部分。

1.初始标记(STW)
2.并发标记
3.重新标记 (STW)
4.并发清除

其中初始标记和重新标记这两个阶段程序的工作线程是停止的Stop The World,而并发标记和并发清除阶段是GC线程和程序工作线程同时运行。

初始标记紧紧是标记与GC Roots能直接关联的对象,并发标记会遍历初始阶段标记的对象,并对他们进行GC Roots Tracing。在并发标记阶段,程序的工作线程还会执行,所以这一阶段GC Roots还会有一点点变化,需要对并发标记的结果进行一些修改,随后就是并发清除。并发标记和并发清除最耗费时间,初始标记只要标记和GC Roots直接相关的就好,停顿时间不长,而重新标记阶段是在并发标记的基础上做的,是对结果的修修改改也花费不了长时间。这样CMS就达到了系统停顿时间短的目标。

关于初始标记和并发标记,假设GC Roots是对象A, 对象A中有对象B和对象C,而对象B和对象C中分别由E/F。当初始标记的时候,只会标记B和C,而E、F不处理,因为A和E/F不是直接关联,中间还有B和C。所以E和F是在并发标记阶段进行GC Roots Tracing时标记。E和F还有可能包含其他的对象,这样遍历下来就是一张大网,肯定耗费时间,所以在并发标记阶段处理,顾名思义肯定是采用多个线程来进行Tracing,这样速度才会快些。而清除阶段,就是清空对象内存,也采用并发加快速度。

CMS的优点就是停顿时间短,并发搜集。而缺点也和这些优点密切相关。

1.对CPU资源非常敏感。CMS是并发执行,在并发标记和并发清除阶段工作线程和GC线程会同时运行,这些线程会抢CPU。CMS默认开启(CPU数量+3)/4的回收线程数,这个当CPU很大是大概是25%,而当CPU为2时结果时1,会占用50%的CPU资源。这个对工作线程的执行速度影响还是很大的。(个人觉得就算时影响50%的速度,那也比Serial Old处理器GC阶段工作线程完全停止好吧?)

2.就是在垃圾清理阶段工作线程还是会进行,但是已经过了重新标记,所以在垃圾清理阶段GC线程一边在清理垃圾,而工作线程可能还产生新的垃圾(浮动垃圾),浮动垃圾只能留给下一次GC时进行标记。同样的因为需要运行动作线程,所以必须预留一定的空间给他们,所以并不能向其他收集器一样等内存完全满了之后再进行GC。JDK1.6默认内存使用92%就会启动GC。极端情况下,万一预留的这部分内存不够用呢?就会出现“Concurrent Mode Failure”失败,这时CMS就处理不了。虚拟机就会启动备用方案,临时启动Serial Old收集器来重新进行老年代的垃圾收集,停顿时间边长了。

3.CMS使用标记-清理,这个算法的缺点就是会产生很多的内存碎片,当没有足够大的连续空间分配对象时,不得不进行一次Full GC。CMS默认开启了内存碎片的整合,内存整合是无法并发的,内存空间碎片问题没有了,但停顿时间不得不变长。CMS还可以设置执行多少次内存不压缩Full GC 然后跟着来一次压缩的。

G1 收集器

G1收集器在新生代和老年代都可以使用,而上面的几款收集器必须搭配使用。

G1虽然也分为新生代和老年代,但新生代和老年代不再是物理隔离的了。它把Java对划分成了多个大小相等的独立区域(Region),新生代和老年代是一部分Region的集合。每个Region都会有一个Remembered Set,用来存储

这篇关于JVM的各种垃圾收集器的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!