JVM垃圾回收器原理

一、内存垃圾回收

我们都知道,在Java内存模型中,虚拟机栈、本地方法栈以及程序计数器是线程私有的。这部分内存随着线程或者方法的结束,内存自动释放。而对于线程共享的堆和方法区来说,一个接口中多个实现类的内存是不一样的,很难分辨出,位于堆内存中的哪些对象是没用的。只有在程序运行的时候,才知道创建了哪些对象。而堆内存又是JVM内存中最大的部分,必须对这部分进行内存管理,否则就会造成内存溢出。

因此,这部分内存的分配和回收都是动态的,所以垃圾回收器关注的就是这部分内容。

二、判断对象存活的算法

垃圾回收器在回收堆内存时,需要先判断哪些对象存活,哪些对象死亡(指堆中的对象没有被任何变量或常量引用)。

1.引用计数算法

顾名思义,就给在创建对象的时候,额外给每个对象加一个引用计数器,当该对象被引用时,这个引用计数器就+1;当引用取消时,引用计数器就-1。但是JVM并没有采用这种算法来判断对象是否死亡,因为这中算法并没有解决对象之间循环互相引用的问题。

public class QuoteNum {
    private  QuoteNum instanse = null;
    private static  final  int _1MB = 1024*1024;
    //  为了占点内存  看垃圾回收效果
    private byte[] bigSize = new byte[2 * _1MB];
    public static void main(String[] args) {
        QuoteNum q1 = new QuoteNum();
        QuoteNum q2 = new QuoteNum();
        // 互相引用  
        q1.instanse = q2;
        q2.instanse = q1;
        // 取消q1 q2 的引用   但此时引用计数器并不为0
        q1 = null;
        q2 = null;
        // 此时再也无法访问到这两个对象
        // 提醒JVM回收
        System.gc();
    }
}

如果采用引用计数算法,那么两个对象是无法进行回收的,通过设置JVM参数,查看其GC日志发现,这两个对象已经被回收。可以得出,JVM判断对象是否死亡并没有采用引用计数算法。

运行结果:
在这里插入图片描述

2. 可达性分析算法

实际上JVM判断对象是否可以被回收采取的是可达性分析算法。基本思路就是通过一系列称为GC-Roots的对象作为起始点,通过这些点向下搜索,形成一系列的引用链。如果一个对象到GC-Roots不存在引用链,就说明这个对象需要被垃圾回收。

(1) 内存回收第一步

在Java语言中可作为GC-Roots的对象包括以下几种

  1. 虚拟机栈中本地变量表引用的对象
  2. 方法区中类静态属性引用的对象
  3. 方法区常量引用的对象
  4. 本地方法栈中引用的对象

在这里插入图片描述如图所示:对象4、5、6、没有到达GC-Roots的引用链,就有可能别垃圾回收器回收。

(2) 内存回收第二步

即使对象不可达GC-Roots,也不能确定该对象所占的内存必须被回收,要真正宣告一个对象的死亡,必须经历两次标记的过程。第一次为不可达分析算法中的标记,第一次标记后,将会进行第二次筛选,筛选的条件是否需要执行finalize()方法。如果需要执行则加入F-queue中,由虚拟机创建一个线程执行这个方法。
对象逃脱死亡的最后依次机会,在执行该方法时,对象可以将自己的赋值给某个类变量或者对象的成员变量。否则进行第二次标记,等待垃圾回收器回收。

演示代码:

public class GCtest {
    public static GCtest gCtest = null;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("执行finalize方法");
        GCtest.gCtest = this;
    }
    public void  alive(){
        System.out.println("我还活着");
    }
    public static void main(String[] args) throws InterruptedException {
        gCtest = new GCtest();
        gCtest = null;
        System.gc();
        Thread.sleep(500);
        if(gCtest != null)
            gCtest.alive();
        else
            System.out.println("垃圾回收器已经回收GCTest对象");
        // 因为一个对象的finalize()方法只能执行依次 所以这次被回收了
        gCtest = null;
        System.gc();
        Thread.sleep(500);
        if(gCtest != null)
            gCtest.alive();
        else
            System.out.println("垃圾回收器已经回收GCTest对象");

    }
}

运行结果:

在这里插入图片描述

发布了66 篇原创文章 · 获赞 26 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Time__Lc/article/details/90183270