深入拆解虚拟机(八)垃圾回收(上)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lwl2014100338/article/details/84197262
引用计数法

(1)它的做法是为每个对象添加一个引用计数器,用来统计指向该对象的引用个数。一旦某个对象 的引用计数器为0,则说明该对象已经死亡,便可以被回收了。

(2)具体实现:如果有一个引用,被赋值为某一个对象,那么该对象的引用计数器+1。如果指向某一个对象的引用,被赋值为其他值,那么该对象的引用计数器-1。也就是说,我们需要截获所有的引用更新操作,并且相应地增减目标对象的引用计数器

(3)除了需要额外的空间来存储计数器,以及繁琐的更新操作,引用计数法还有一个重大的漏洞,那便是无法处理循环引用对象


可达性分析算法

(1)算法实质是将一系列GC Roots作为初始的存活对象合集,然后从合集触发,探索所有能够被该集合引用到的对象,并且将其加入到集合中,这个过程我们称之为标记(mark)。最终,未被探索到的对象便是死亡的,是可以被回收的

(2)GC Roots:我们暂时可以理解为由堆外指向堆内的引用,一般而言,GC Roots包括(但不限于)如下几种:

  • Java方法栈帧中的 局部变量
  • 已加载类的静态变量
  • JNI handles
  • 已启动且未停止的Java线程

(3)可达性分析算法在实践中还有不少其他问题需要解决,比如说,多线程环境下,其他线程可能会更新访问过的对象中的引用,从而造成误报(将引用设置为null)或者漏报(将引用设置为未被访问过的对象)

(4)误报并没有什么伤害,Java虚拟机至多损失部分垃圾回收的机会。漏报比较麻烦,因为垃圾回收器可能回收事实上扔被引用的对象内存。一旦原引用访问已经被回收了的对象,则很有可能会导致Java虚拟机奔溃


Stop-the-world以及安全点

(1)Java虚拟机中Stop-the-world是通过安全点机制来实现的。当Java虚拟机收到Stop-the-world请求,它便会等待所有的线程都到达安全点,才允许请求Stop-the-world的线程进行独占的工作

(2)安全点的初始目的并不是让其他线程停下,而是找到一个稳定的执行状态。在这个执行状态下,Java虚拟机的堆栈不会发生变化。这样一来,垃圾回收器便能够’'安全 ''地执行可达性分析


垃圾回收的三种方式
清除

(1)清除,即把死亡对象所占据的内存标记为空闲内存,并记录在一个空闲列表中。当需要新建对象时,内存管理模块便会从该空闲内存列表中寻找空闲内存,并划分给新建的对象

(2)缺点:①造成内存碎片。由于Java虚拟机堆中对象必须是连续分布的,因此可能出现总空闲内存不够,但无法分配的极端情况。② 分配效率较低,如果对于一块连续的内存空间,那么我们可以通过指针加法来做分配。而对于空闲列表,Java虚拟机则需要逐个访问列表中的项,来查找能够放入新建对象的 空闲内存


压缩

即把存活的对象聚集到内存区域的起始位置,从而留下一段连续的内存空间。这种做法可以解决内存碎片化的问题,但是代价是压缩算法的性能开销


复制

(1)即把内存区域分为两等分,分别用两个指针from和to来维护,并且只是用from指针指向内存区域来分配内存。

(2)当发生垃圾回收时,便把存活的对象复制到to指针指向的内存区域中,并且交换from指针和to指针的内容。

(3)复制 这种回收方式能够解决内存碎片化的问题,但是它的缺点也及其明显,极堆空间的使用效率及其低下


猜你喜欢

转载自blog.csdn.net/lwl2014100338/article/details/84197262