对象存活判定和垃圾收集算法

对象存活判定算法

引用计数算法

在对象中添加一个引用计数器,每当有一个地方引用它时,计时器值加一;当引用失效时,计数器就减一;任何时刻计数器为零的对象就是不可能再被使用的。

Java虚拟机不是通过引用计数算法来判断对象是否存活。

可达性分析法

Java虚拟机通过可达性分析算法来判定对象是否存活。可达性分析算法基本思路是通过一系列称为GC ROOTS的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程锁走过的路径称为引用链(Reference Chain),如果某个对象到GC ROOTS间没有任何引用链相连,或者从GC ROOTS到这个对象不可达时,则证明此对象是不可能再被使用的。

引用

如果Reference类型的数据中存储的数值代表的是另一块内存的起始地址,就称该Reference数据是代表某块内存、某个对象的引用。

JDK1.2版本后,将引用分为强引用(Strongly Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)

  • 强引用

    强引用是最传统的引用的定义,是指在程序代码之中普遍存在的引用赋值,类似:Object obj = new Object();这种引用关系。无论任何请款下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象

  • 软引用

    软引用用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。JDK1.2之后提供了SoftReference类来实现软引用。

  • 弱引用

    弱引用用来描述非必须的对象,比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。JDK1.2之后提供了WeakReference类来实现弱引用。

  • 虚引用

    虚引用是最弱的一种引用关系。一个对象是否有虚引用存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象的实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。JDK1.2之后提供了PhantomReference类来实现虚引用。

finalize()方法

finalize()方法是类Object的方法,所有对象都有,任何对象的finalize()方法都只会被系统自动调用一次。一个对象在可达性分析算法判定为不可达对象并不一定会被垃圾收集回收,最多会经历两次标记过程:

  1. 如果对象在可达性分析后发现没有GC ROOTS相连接的引用链,将会对对象进行第一次标记,随后进行一次筛选,筛选的条件是此对象是否有必要执finalize()方法。如果对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机调用过,则没有必要执行
  2. 如果判断对象有必要执行finalize()方法,该对象会被防止一个名为F-Queue的队列中,并在稍后由一条虚拟机自动创建、低优先级的Finalizer线程去执行它们的finalize()方法。稍后收集器对F-Queue队列中的对象进行第二次小规模标记,如果对象在finalize()方法中从新建立引用链,那么在第二次标记时它将被移除即将回收的集合。

演示虚拟机自动调用finalize()方法

虚拟机堆内存大小设置:-Xmx1m -Xms1m

public class Demo {

    public static void main(String[] args) {
        int i = 0;
        while (true){
            Data d = new Data();
            System.out.println(i ++);
        }
    }
}

class Data{

    @Override
    protected void finalize() throws Throwable {
        //这里如果 外部的引用 = this; 对象将完成自我救赎不会被回收
        System.out.println("对象被回收===========");
        super.finalize();
    }
}

finalize()方法运行代价高昂,不确定性大,无法保证各个对象的调用顺序,官方不推荐使用。

垃圾收集算法

分代收集理论

  • 弱分代假说:绝大多数对象都是朝生夕灭的。
  • 强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡。
  • 跨代引用假说:跨代引用相对于同代引用来说仅占极少数。

收集器将Java堆划分出不同区域,根据回收对象的年龄(对象熬过垃圾收集过程的次数)分配到不同的区域中存储。

一个区域中大多数对象都是朝生夕灭,难以熬过垃圾收集过程的话,把他们集中放在一起,每次回收时只关注如果保留少量存活的对象,而不是标记大量将要被回收的对象,就能以较低代价回收大量的空间(新生代);剩下的=难以消亡的对象,把他们集中放在一起,虚拟机便可以使用较低的频率来回收这个区域(老年代),同时兼顾了垃圾收集的时间开销和内存空间的有效利用。

Java堆至少会分为**新生代(Young Generation)老年代(Old Generation)**两个区域,在新生代中,每次垃圾收集时都发现有大批对象死去,而每次回收后存活的少量对象,将会逐步晋升到老年代中存放。

标记—清除算法

标记清楚算法分为标记清除两个阶段,首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象。也可以返回来,标记存活的对象,统一回收所有未被标记的对象。

在这里插入图片描述

标记清除算法主要有两个缺点

  1. 执行效率不稳定,当Java堆中包含大量对象,其中大部分是需要被回收的,这时必须进行大量标记和清除动作,导致标记和清除两个过程都随对象数量的增长而降低
  2. 标记、清除后会产生大量的内存碎片,空间碎片太多可能导致以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作

标记—复制算法

标记复制算法简称复制算法。为了解决标记—清楚算法面对大量可回收对象时执行效率低的问题提出的一种算法。复制算法将可用内存按容量划分为大小相等的两块,每次只是用其中的一块。当一块内存用完了,就将还活着的对象复制到另外一块上面,然后再把使用过的内存一次清理掉

如果内存中多数对象都是可回收的情况,算法需要复制的就占少数的存活对象,而且每次都是针对整个半区进行内存回收,分配内存时也就不用考虑空间碎片的复杂情况,只要移动堆顶指针,按顺序分配即可。

复制算法实现简单,运行高效,缺陷是将可用内存缩小为原来的一半,空间浪费太多

商用的Java虚拟机大多都优先采用标记复制算法回收新生代

在这里插入图片描述

标记—整理算法

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

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值