深入理解JVM5:GC篇(回收算法)

此篇博客主要以笔记的形式,记录笔者在B站《深入理解JVM》课程中学到的知识点。课程地址:《黑马程序员JVM完整教程,全网超高评价,全程干货不拖沓》

1.判断对象可回收


1.1.引用计数法

定义: 记录每个对象被引用的次数,当被引用次数为零时,则可以认为该对象是无用的对象,可以被gc进行回收处理。(如:A对象被B、C两个对象同时引用,则A对象对应的被引用次数为2;若B、C两个对象不再引用A对象,导致A对象被引用次数变为0,此时A对象将会被视为“垃圾”,而被回收)

不足之处: 当对象间产生循环依赖时,无法对其进行垃圾回收处理~!!(如下图)

因为对象A引用了对象B,同时对象B又引用了对象A,但两个对象却独立程序而存在(即:两个对象在实际程序中并没有任何用处,是独立程序而存在的),这时这两个对象都应该被视为“垃圾”,但是A、B两者间互相引用,被引用次数都为1,此时引用计数法认为二者都不是“垃圾”,所以并不会对这两个对象进行垃圾回收处理。

为了解决这个问题,我们需要使用接下来的算法——可达性分析算法。


1.2.可达性分析算法

定义: 通过一系列被称为「GC Roots」的根对象作为起始节点集,从这些节点开始,通过引用关系向下搜寻,搜寻走过的路径称为「引用链」,如果某个对象到GC Roots没有任何引用链相连,就说明该对象不可达,即可以被回收

可能直接看定义理解起来会很困难,这里笔者引用课程中的老师使用的一个比喻。将所有的对象和对象之间的引用想成一串葡萄,每个对象对应每颗葡萄,而连接葡萄的根则是对象间的引用关系。当你从盘子上拎起一串葡萄时,总会有那么几颗葡萄脱离根而独立存在落在盘子中,而这些葡萄在可达性分析算法中就是需要回收的“垃圾”,这样我们就可以有效解决对象间循环引用的问题。

怎样的对象才会被认为是GC ROOT

  1. 虚拟机栈中的本地变量所引用的对象。
  2. 方法区中静态属性引用的对象。
  3. 方法区中常量引用的对象。
  4. 本地方法中(Native方法)引用的对象。
  5. 虚拟机内部的引用对象(类记载器、基本数据对应的Class对象,异常对象)。
  6. 所有被同步锁(Synchronnized)持有的对象。
  7. 描述虚拟机内部情况的对象(如 JMXBean、JVMTI中注册的回调、本地缓存代码)。
  8. 垃圾搜集器所引用的对象。


1.3.四种引用

1.3.1.强引用: 当该对象没有被任何的GC ROOT对象引用时,该对象才能被回收。
在这里插入图片描述


1.3.2.软引用(SoftReference): 仅有软引用来引用该对象时,在垃圾回收之后,如果内存仍然不足,则会再次出发垃圾回收机制,回收软引用对象。(可以配合引用队列俩释放软引用自身)
在这里插入图片描述


1.3.3.弱引用(WeakReference): 仅有弱引用来引用该对象时,在垃圾回收时,无论内存时候充足,都会回收该弱引用对象。(可以配合引用队列俩释放弱引用自身)


1.3.4.虚引用(PhantomReference): 必须配合引用队列使用,主要配合ByteBuffer使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用相关方法释放内存。


1.4.软引用的应用例子

1.4.1.未使用引用时:

// 未使用引用的例子
List<byte[]> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
    list.add(new byte[1024 * 1024 * 10]);
}

1.4.2.使用软引用(SoftReference):

// 使用软引用例子
List<SoftReference<byte[]>> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
    list.add(new SoftReference<>(new byte[1024 * 1024 * 10]));
}

1.4.3.使用引用队列(ReferenceQueue)

// 引用队列
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
// 软引用
List<SoftReference<byte[]>> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
    // 绑定引用队列
    list.add(new SoftReference<>(new byte[1024 * 1024 * 10], queue));
}

2.回收算法


2.1.标记-清除算法

定义: 对需要进行垃圾回收处理的对象进行标记,当垃圾回收机制被触发时,被标记的对象将会被清除。

优点: 简单直接,速度也非常快,特别适合可回收对象不多的场景。

缺点: 会产生内存碎片。如下图将需要回收的对象清除后,两个GC ROOT之间存在一小块未被占用的内存空间。如果此时添加一个占用内存刚好等于未被占用内存之和的大对象,是无法将该对象存入JVM中的,虽然总的内存大小刚好与该对象的占用内存相等,但由于未被占用的内存是不连续的,无法直接将该大对象存入。


2.2.标记-复制算法

定义: 按照内存划分两块大小一样的内存空间(分别称为from区和to区,其中to区在触发GC前,保持内存占用为空的状态,为触发GC时做准备),当GC触发时,from区中需要保留的对象将会复制到to区中,并且复制过去的对象内存空间是连续的,复制完成后清除from区中的对象剩余对象,并且两个区域进行逻辑上的互换(并非空间互换)

优点: 解决了标记清除法的空间碎片问题,并且采用移动存活对象的方式,每次清除针对的都是一整块内存,所以清除可回收对象的效率也比较高,但因为要移动对象所以这里会耗费一部分时间,所以标记复制法还效率还是会低于标记清除法。

缺点: 需要开辟to区空间,而这段空间是为下一次触发GC时准备的,当GC未被触发时,这段空间是被浪费掉的。


2.3.标记-整理算法

定义: 标记整理法主要分为三个阶段(标记、整理和清除)。标记阶段将需要清除整理的对象标记起来;整理阶段将无需回收处理的对象内存地址向前挪动;清除阶段将标记起来的对象清除。

优点: 解决了标记清除算法产生内存碎片的问题,同时也不至于像标记复制法需要空闲的内存空间,所以它非常适合存活对象多的场景。

缺点: 标记整理法是三种垃圾回收算法中性能最低的一种,因为标记整理法在移动对象的时候不仅需要移动对象,还要额外的维护对象的引用的地址,这个过程可能要对内存经过几次的扫描定位才能完成,做的事情越多那么必然消耗的时间也越多。


2.4.分代回收

在实际的虚拟机设计中,上述的三种算法往往都会在虚拟机中结合各个算法间的优缺点混合使用,而分代回收就是其中最典型的设计。

定义: 分代回收在大体上分为新生代老年代,其中新生代细分为一块伊甸园Eden(新生区),和两块幸存区FromTo(在其他的文章中还会也称为s0s1)。

具体执行逻辑如下:

  1. 当线程执行时,会产生新的对象,并放入Eden区,当新生代内存不足时,触发minor GCEdenfrom存活的对象copy到to中,存活的年龄加1并且交换from to
  2. minor GC 会触发 stop the world ,暂停其它用户线程,等待回收完成才会发执行
  3. 当对象的年龄达进入老年代设置的阈值时,对象就会进入老年代(默认最大寿命是15
  4. 当老年代空间不足时。会先触发minor GC,如果仍不足,会触发Full GC,由于老年代对象比较多,也不容易被回收,所以线程等待的时间可能更长些
  5. 如果 触发Full GC回收时仍然不足,则会抛出内存溢出异常

换成白话文再来描述一遍:

  1. 当线程产生新的对象时,首先会放到伊甸园区域中(西方神话中人类始祖诞生的地方),当新生的对象越来越多了,伊甸园已经无法容纳这么多对象,就需要将无用的对象进行清理,对应的操作称为minor GC。而此时存活下来的对象将会进入到幸存区中。
  2. 值得注意的是: 触发minor GC 的同时也会触发stop the world,英文直译为世界暂停。实际的作用其实就是在垃圾回收时,停止除了垃圾回收线程外的所有线程操作。因为如果在垃圾回收时不暂停其他线程,对象之间的引用还在发生变化(对象的引用地址可能会被其他的线程改变),那么这个算法几乎没法执行,所以常常需要STW来维持秩序。
  3. 经过第一步的minor GC后,幸存下来的对象仍然存在被清理的可能。在幸存区中的对象,每经历一次垃圾回收并存活下来,那么该对象的“寿命”将会+1,而当对象的寿命达到15岁(4bit)时,算法会认为该对象是“骨干分子”,将会把该对象晋升到老年代中。
  4. 当老年代的空间也被占满时,并不会马上对老年代中的对象进行回收操作,而是优先对新生代进行一次minor GC,若进行此步操作后空间仍然不足,算法才会对老年代进行垃圾回收Full GC操作,由于老年代对象比较多,也不容易被回收,所以线程等待的时间可能更长些。
  5. 如果触发Full GC回收后空间仍然不足,则会抛出内存溢出异常。

新生代和老年代,占用堆区的比例是:1:2

在新生代中的伊甸园区和幸存者from区、to区的占比是:8:1:1


3.GC相关参数


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云丶言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值