JVM学习笔记(三)——垃圾回收机制(GC)

在Java世界里,几乎所有的对象都在堆中。堆的空间不是无限的,总有满的一天,被垃圾收集器清除掉是对象们的宿命。

那么,对象到了要被清理的时候,垃圾收集器要如何去确定哪些对象是需要清除的呢?

 

如何确定一个对象是垃圾

1.引用计数法

在对象中添加一个引用计数器,每当此对象被引用时,计数器值就加一;当引用失效时,计数器值就减一;当引用为零时,说明此对象不会再被使用了,到了该清理的时候了。

但是,JVM里面是不用这种方法的,原因就是对象的相互引用问题无法解决。如下所示:相互引用,但其实最后是无用的。

A a = new A();
B b = new B();

a.object = b;
b.object = a;

a = null;
b = null;

2.可达性分析算法

通过名为GC Root的根对象作为起始节点集,根据引用关系向下搜索,搜索过程所走过的路径称为引用链。如果某个对象到GC Roots间没有任何引用链相连(也就是从GC Root无法到达此对象),则说明此对象是不可能再被使用的。能作为GC Root的为:类加载器,Thread,虚拟机栈的本地变量表,static成员,常量引用,本地方法栈的变量等。

如图,对象D,E,F存在关联,到时不存在到达GC Root的路径,所以这三个对象是无用的对象。

 

无用对象找到了,该清理时就清理,那要如何清理呢?

 

垃圾收集算法

1.标记-清除(Mark-Sweep)算法

将需要清理的对象进行标记,然后根据标记来讲对象清理掉。

清理完成后:

缺点:

1.效率。对象数量越多,则需要标记和清除的数量也随之增加。效率会随着对象数量的增长而降低。

2.会产生大量不连续的空间碎片。当空间碎片太多,又有大空间的对象进来后就没有足够的连续空间可以分配,不得不提前触发垃圾收集。

 

2.标记-复制算法(Semispace Copying)算法

为解决标记-清除算法的缺陷,出现了标记-复制的算法,这种算法是将内存空间划分为两块相等的区域,每次只使用其中一块。当已使用的这块内存用完了,就

将当前这块内存里面还存活的对象复制到空的那块内存中去,然后执行清理操作。

清理完成后:

复制过去时,会按顺序来分配,所以的确是解决了空间碎片的问题。

缺点:

因为可以使用的内存容量只有以前的一半,空间浪费太大了。

 

3.标记-整理(Mark-Compact)算法

标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。

清理后:

缺点:

即使解决了前两种算法的缺陷,但终归标记-整理也存在自己的缺陷:移动存活对象时,比如对象数量对象很大的老年代,移动并更新所有对象的引用是

一件负担很大的事情,而且这种操作必须全程暂停用户应用程序。所以并不是有缺点的垃圾回收算法就不能用,而是要在不同场景选择不同的算法。

 

不同区域使用的收集算法

堆里面分为Young区和Old区,这两个区是如何选择收集算法的。

Young区

使用标记-复制算法,对象分配在Young区后的生命周期会比较短,更新迭代快,使用复制算法效率高。

Old区

使用标记-清除或整理算法,对象在Old区呆的时间是比较长的,在两块内存空不断来回复制显然不合适。

 

垃圾收集器

用收集算法还不行,还要有东西来进行实现。

 

1.Serial

Serial收集器是最基础、历史最悠久的收集器,在JDK 1.3.1之前是HotSpot虚拟机新生代收集器的唯一选择。是一个新生代的垃圾收集器,使用算法为标记-复制算法

它是一个单线程的垃圾收集器,但这个单线程不是指用一个处理器或者一条线程来执行,而是垃圾收集的时候会暂停其它的工作线程直到收集结束。

但Serial的简单高效是优于其它垃圾收集器的,而且内存消耗也是最小的。并且Serial由于没有线程交互的开销,只专心做垃圾收集,所以是能获得最高的单线程收集效率。

现在的应用很多微服务化,分配给虚拟机的内存不会太多,收集几十兆或一两百兆的新生代停顿时间是可以控制在毫秒级的。

 

2.ParNew

Serial收集器的多线程并行版本。同样是是一个新生代的垃圾收集器,使用算法为标记-复制算法。

多CPU环境下效率高于Serial,单CPU情况下效率低于Serial

 

3.Parallel Scavenge

同样是新生代收集器,基于标记-复制算法实现的收集器,也是能够并行收集的多线程收集器。但是Parallel Scavenge更关注系统的吞吐量。

吞吐量大意味着在相同时间内能收集更多的垃圾,效率自然也高。

提供了两个可以用户自己设置的参数:

-XX:MaxGCPauseMillis 控制最大的垃圾收集停顿时间
-XX:GC Time Ratio 直接设置吞吐量的大小
 
 

4.Serial Old

Serial 收集器的老年代版本,同样是单线程收集器,不同的是采用 标记 - 整理算法 ,运行过程和 Serial 收集器一样。
 
 

5.Parallel Old

Parallel Scavenge 收集器的老年代版本,同样的是多线程,不同的是 标记 - 整理算法
 
 

6.CMS(Concurrent Mark Sweep)

使用标记-清除法,以最短回收停顿时间为目标的一款垃圾收集器。

整个收集过程分为四步:

初始标记(CMS initial mark)

并发标记(CMS concurrent mark)

重新标记(CMS remark)

并发清除(CMS concurrent sweep)

因为并发标记和并发清除,收集器线程可以与用户线程一起工作,所以CMS收集器的内存回收过程是与用户线程一起并发地执行的

CMS的主要优点为并发收集、低停顿,因为算法是使用的标记-清除法,所以缺点显而易见,会产生大量空间碎片和并发时会降低吞吐量。

 

7.G1(Garbage First)

G1是垃圾收集器技术发展历史上的里程碑式的成果,它开创了收集器面向局部收集的设计思路和基于Region的内存布局形式。

G1之前的垃圾收集器,要么是新生代收集器要么是老年代收集器。G1跳出了这个束缚,可以面向堆内存的任何部分组成回收集来进行回收,

衡量标准不再是属于哪个分代了,而是哪个内存中垃圾数量最多.。

JDK 9发布之日,G1宣告取代Parallel Scavenge加ParallelOld组合,成为服务端模式下的默认垃圾收集器,而CMS则沦落至被声明为不推荐使用(Deprecate)的收集器。

 

如何开启需要的垃圾收集器

更换垃圾收集器需调整jvm的参数
 
-XX +UseSerialGC
-XX +UseSerialOldGC
 
-XX +UseParallelGC
-XX +UseParallelOldGC
 
-XX +UseConcMarkSweepGC
-XX +UseG1GC
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值