李琪的技术专栏 System Research

垃圾回收学习记录

2020-05-01
Clear Li

阅读:


概述

之前说过,程序计数器,虚拟机栈,本地方法栈是随着线程的产生而产生,随线程的销毁而被销毁。因此垃圾回收主要就是针对Java堆中的内存。一个接口中的多个实现类所需要的内存不一样,一个方法中的多个分支需要的内存也不一样,只有在程序创建过程中才能知道实际的内存要创建多少。

引用计数法

那么怎么确定在回收这些对象的时候确定这些方法是否还活着呢,正常来讲会想到,可能是引用计数法。这种方法会给每个对象创建一个引用计数器,每当一个地方引用到他时,计数器的值就会+1;当引用失效的时候,计数器的值就会-1.当计数器的值为0的时候对象就不可能在被使用了。但是假如说两个对象相互引用呢。难道永远都不回收这些对象吗?

可达性分析算法

先记录一下我画的图,因为在老家电脑不太好使,就使用手绘吧。

image-20200501161336601

这种使用的是树的结构,从GCRoot的对象为起始点,向下搜索,找到其他相关联的对象,例如从虚拟机栈中的引用对象向他所引用的对象进行查找。对于另外的虽然也有引用,但是会将他们放入到被GC的队列当中准备回收。

回收时

image-20200501161916446

当一个对象进入垃圾回收队列也是有可能”起死回生“的,假如对象在调用finalize方法时,引用了上面所提到的可达性链上的对象时,将会被重新使用。例如下面代码

public class TestXu {
    public static TestXu SAVE_HOKE = null;
     void isAlive(){
         System.out.println("我还活着");
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize 方法被调用");
        SAVE_HOKE =this;
    }

    public static void main(String[] args) throws InterruptedException {
        SAVE_HOKE =new TestXu();
        SAVE_HOKE =null;
        System.gc();
        Thread.sleep(500);

        Optional.ofNullable(SAVE_HOKE).ifPresent(TestXu::isAlive);
        SAVE_HOKE =null;
        System.gc();
        Thread.sleep(500);
        Optional.ofNullable(SAVE_HOKE).ifPresent(TestXu::isAlive);

    }
}

结果

image-20200501162502337

尽管已经是将要被回收的对象,但是他在 finalize()方法中调用了自己,因此他将重新可以被使用。但是由于 finalize()方法只能被一个对象调用一次,因此他在下一次执行时将会被gc。这种方法书上说并不推荐,因为会带来很多的不确定性。


下一篇 UniApp记录