【JVM】垃圾收集器与内存分配(一)

概述

垃圾收集器应用于堆内存与方法区,因为这两个区域在内存分配时是不确定的,只有在运行期间我们才能知道创建哪些对象,创建了多少对象,这部分的内存分配与回收也是动态的,垃圾回收关注的正式这部分内存该如何管理

引用计数法

在对象中添加一个引用计数器,当有地方引用这个对象时,计数器加一,引用失效时,计数器减一,若计数器的值为零,则这个对象不能被使用。

缺点:当对象A引用了B,B对象引用了A,这两个对象已经不能被访问了,但互相引用导致计数器不为零,引用计数法也就无法回收它们。

可达性分析算法

从“GC roots”开始根据引用关系向下搜索,搜索走过的路径被称为引用链,当一个对象到GC roots之间没有引用链相连接,则这个对象不能被使用,需要被回收。
在这里插入图片描述
可作为GC roots的对象:

  1. 被虚拟机栈引用的对象
  2. 方法区中静态属性引用的对象
  3. 方法区中常量引用的对象

引用分类

1. 强引用:Object object = new Object()
2. 软引用:有用非必须,被软引用关联的对象,在系统将要发生溢出之前,会进行第二次垃圾回收,若回收后内存依然不够,则报OOM。
3. 弱引用:非必须,被弱引用关联的对象只能活到下一次垃圾回收之前,当垃圾收集器开始工作,无论内存是否足够,弱引用的对象都会被回收。
4. 虚引用:不能根据虚引用取得对象实例,只有在对象被回收时收到一个系统通知

finalize()方法

对象在进行可达性分析后发现与GC roots之间没有引用链相连接,则会对其进行第一次标记,随后判断其是否有必要执行finalize方法,如果对象的finalize方法没有被重写或已经被调用过,那么就被视为没必要执行,被垃圾收集器回收。
如果被判定为有必要执行finalize方法,则会将对象放入一个叫F-Queue的低优先级队列中,会有虚拟机创建一个线程去执行它们的finalize方法,若执行后对象有被引用,则被移除回收集合,否则就被回收。
一个对象的finalize方法只会被执行一次,如果这次垃圾回收执行过了,下次垃圾回收时不会再被执行。

回收方法区

方法区主要回收常量和不使用的类,但类型回收比较苛刻,所以方法区的回收性价比不高。

垃圾回收算法

标记清除算法

标记出要回收的对象,随后进行清除。

缺点:

  1. 当一个堆中包含了大量对象,当量对象都要被清除,导致标记和清除的效率降低。
  2. 清除对象后没有进行整理的操作,导致堆中空间碎片太多,但是放如大对象时又需要连续的内存空间,以至于提前进行一次垃圾回收。

在这里插入图片描述

标记复制法

将内存划分为两块大小相等的区域,每次只使用其中一块,当一块内存用完时,将这块中存活的对象复制到另一块上去,随后把前者清理掉。

缺点:一次只使用一半,太浪费。
在这里插入图片描述
Appel式回收
将内存分为新生代和老年代,新生代中分为一块Eden区和两块Survive区(from,to),虚拟机默认比例Eden:Survive为8:1,当发生垃圾回收时,将存活的对象复制到另一块没用的Survive区上,若其内存不够,则需要依赖老年区进行分配担保。

标记整理算法

让存活的对象向内存另一端移动,随后清理掉分界线后的对象。

缺点:这种操作必须暂停掉所有其他线程才能进行。
在这里插入图片描述

HotSpot算法细节实现

根节点枚举

根据可达性分析算法从GC Roots开始搜索引用链。根节点枚举需要暂停用户线程。

安全点

用户程序执行时停顿下来进行垃圾回收的位置
让线程到安全点停顿下来的方案:

  1. 抢先式中断,发生垃圾收集时,全部中断,不在安全点上的点让其继续运行到安全点上再中断
  2. 主动式中断,线程执行过程中不断轮询,发现中断标志后就到离自己最近的安全点上挂起,安全点和轮询点是相同的。

安全区域

如果线程在sleep过程中就不会执行,所以要设置一段安全区,保证线程在这段安全区域内,引用关系不会变化,在离开安全点时要检查虚拟机的根节点枚举等要暂停用户线程的阶段是否结束了,如果没有结束,那等到结束了再出安全区。

记忆集与卡表

为了解决跨代引用,垃圾收集器再新生代中建立了记忆集,记忆集是用于记录非收集区域指向手机区域的指针集合的抽象数据结构。
记录全部含跨代引用的对象信息成本太高,所以收集器只需要通过记忆集来判定是否有非收集区域指向收集区域的指针就可以了。因此用了卡精度(每个记录精确到一块内存区域,该区域中含有跨代指针)来记录,卡精度用卡表去实现记忆集,卡表的形式是一个字节数组,其中的元素指向了内存区域中一块特定大小的内存块——卡页。
一个卡页中不止包含了一个对象,当一个卡页中有一个对象存在着跨代指针,那就将数组元素的值标志为1,称这个元素变脏,在垃圾回收时只要将这些变脏的元素加入到GC Roots中一并扫描。
在这里插入图片描述

写屏障

在引用对象赋值后进行引用的更新。(我觉得类似于Spring中使用AOP的方式对被增强方法进行增强)
在赋值前叫做写前屏障,写后叫做写后屏障(G1出现前,其他收集器都用写后屏障)。

并发的可达性分析

并发过程中,对象的引用关系会发生改变
在这里插入图片描述
所以为了解决并发时对象引用的问题,有两种解决方案,增量更新、原始快照

增量更新:当黑色对象插入新的指向白色对象的引用时,将这个引用记录下来,再扫描完成后,以记录引用的黑色对象为根,重新扫描一次。

原始快照:当灰色对象删除指向白色对象的引用时,记录下这个引用,扫描完成后,将这个引用中的灰色对象为根,重新扫描。

CMS使用增量更新,G1使用原始快照

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值