从finalize的执行来看什么是GC Root

首先我们来了解一下finalize方法,finalize由对象上的垃圾回收器在垃圾收集确定没有更多对该对象的引用(GC root不可达)时调用。子类重写finalize方法来处置系统资源或执行其他清理工作。

finalize

added in API level 1
protected void finalize ()

Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the finalize method to dispose of system resources or to perform other cleanup.

了解一下它的某些性质,先不必知道为什么会这样:

  1. finalize方法由名为“Finalizer”的守护线程执行,只会被调用一次
  2. 在finalize方法中,可将待回收对象赋值给GC Root从而达到对象再生的目的

之后我们了解下GC Root,GC Root即Garbage collection root,Android开发者官网GC Root的阐述如下:

There are several kinds of garbage collection roots in Java:

  • references on the stack
  • Java Native Interface (JNI) native objects and memory
  • static variables and functions
  • threads and objects that can be referenced
  • classes loaded by the bootstrap loader
  • finalizers and unfinalized objects
  • busy monitor objects

在JAVA中有如下几种GC Root:

  1. 在栈上的引用
  2. JNI本地对象和内存
  3. 静态变量和函数
  4. 线程和可以被引用的对象
  5. 被bootstrap loader加载的类
  6. finalizers和unfinalized的对象
  7. 被占用的监视器对象

我们知道GC对垃圾对象的判断依据是对象是否从GC Root可达,结合finalize方法的API解释,可知对象不可达时会调用对象的finalize方法。接下来我们用代码来验证一下上述几种GC Root:

一、静态变量

public class RootTest {
    static int gcTime = 0;
    
    //静态变量
    static RootTest rootTest = null;

    public static void main(String[] args) throws InterruptedException {
        rootTest = new RootTest();
        gc();
        Thread.sleep(1000);

        gcTest();
        gcTest();
    }

    static void gcTest() throws InterruptedException {
        rootTest = null;
        gc();
        Thread.sleep(1000);
        if (rootTest == null)
            System.out.println("rootTest dead");
        else
            System.out.println("rootTest alive");
    }

    static void gc() {
        System.gc();
        System.out.println("gc " + ++gcTime);
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("RootTest finalize() " + Thread.currentThread().getName());
        rootTest = this;
    }
}

我们使用了静态变量rootTest引用RootTest对象后立即执行一次gc,然后执行两次gcTest:

  1. 将rootTest置为null
  2. 强制执行gc
  3. 休息1s等Finalizer线程执行finalize方法
  4. 判断rootTest状态

运行结果:

gc 1
gc 2
RootTest finalize() Finalizer
rootTest alive
gc 3
rootTest dead

可见,第一次执行gc后,GC发现RootTest对象是可达的,RootTest对象的finalze方法没有被调用。

第二次将rootTest置为null后,执行gc时GC发现RootTest对象不可达,RootTest对象的finalze方法被调用,finalze中RootTest对象再生。

第三次将rootTest置为null后,执行gc时GC发现RootTest对象不可达,RootTest对象的finalze方法被调用过了,这次不会再被调用,RootTest对象被回收。

二、线程、栈上的引用

public class RootTest {
    static int gcTime = 0;

    public static void main(String[] args) throws InterruptedException {

        //栈上的引用
        Thread thread1 = new Thread() {
            @Override
            public void run() {
                System.out.println("Thread1 run over");
            }

            @Override
            protected void finalize() throws Throwable {
                super.finalize();
                System.out.println("Thread1 finalize() " + Thread.currentThread().getName());
            }
        };
        thread1.start();

        new Thread() {
            @Override
            public void run() {
                System.out.println("Thread2 run over");
            }

            @Override
            protected void finalize() throws Throwable {
                super.finalize();
                System.out.println("Thread2 finalize() " + Thread.currentThread().getName());
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                int x = 0;
                while (x < 3) {
                    System.out.println("Thread3 run " + ++x);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("Thread3 run over");
            }

            @Override
            protected void finalize() throws Throwable {
                super.finalize();
                System.out.println("Thread3 finalize() " + Thread.currentThread().getName());
            }
        }.start();

        gc();
        Thread.sleep(5000);
        gc();

        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                gc();
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    static void gc() {
        System.gc();
        System.out.println("gc " + ++gcTime);
    }
}

在main方法中起三个子线程,第一个执行打印后立即结束运行但是会被栈中的局部变量引用,第二个执行打印后立即结束运行,第三个会循环三次打印,接着执行三次gc

运行结果:

Thread1 run over
Thread2 run over
Thread3 run 1
gc 1
Thread2 finalize() Finalizer
Thread3 run 2
Thread3 run 3
Thread3 run over
gc 2
Thread3 finalize() Finalizer
gc 3
Thread1 finalize() Finalizer

首先子线程Thread1运行结束,Thread2运行结束,Thread3开始执行

然后,在Thread3运行期间第一次执行gc,可以看到Thread2的finalze方法被调用

然后,在Thread3运行结束后第二次执行gc,可以看到Thread3的finalze方法被调用

然后,当main方法运行结束后第三次执行gc,可以看到Thread1的finalze方法被调用

三、被占用的监视器对象

public class RootTest {
    static int gcTime = 0;

    public static void main(String[] args) throws InterruptedException {

        new UnusedLock();

        new Thread() {
            @Override
            public void run() {
                int x = 0;
                synchronized (new UsedLock()) {
                    while (x < 3) {
                        System.out.println("Thread run " + ++x);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("Thread run over");
                }
            }

            @Override
            protected void finalize() throws Throwable {
                super.finalize();
                System.out.println("Thread finalize() " + Thread.currentThread().getName());
            }
        }.start();

        gc();
        Thread.sleep(5000);
        gc();
        Thread.sleep(5000);
    }

    static void gc() {
        System.out.println("gc start " + ++gcTime);
        System.gc();
        System.out.println("gc over " + gcTime);
    }
}


class UsedLock {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("UsedLock finalize()");
    }
}

class UnusedLock {
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("UnusedLock finalize()");
    }
}

直接new一个不会被占用的监视器对象,然后启动一个循环打印三次的子线程,并在其中使用一个被其占用的监视器对象,接着执行两次gc

运行结果:

gc start 1
Thread run 1
gc over 1
UnusedLock finalize()
Thread run 2
Thread run 3
Thread run over
gc start 2
gc over 2
UsedLock finalize()
Thread3 finalize() Finalizer

首先,在Thread3执行期间第一次执行gc,可以看到UnusedLock的finalze方法被调用

然后,当Thread3执行完成后第二次执行gc,可以看到UsedLock和Thread的finalize方法都被调用了

GC Roots(垃圾回收根对象)是 JVM 垃圾回收器判断对象是否可达的起点。只有从 GC Roots 出发能够“可达”的对象才会被保留,不可达的对象则会被判定为可回收的垃圾。 ### GC Roots 的来源 GC Roots 并不是指某个单一对象,而是一组**具有强引用、不会被随意回收的特殊对象或位置**,它们是垃圾回收算法(如可达性分析算法)的起始点。以下是 JVM 中常见的 GC Roots 来源: --- #### 1. **虚拟机栈(本地变量表)中引用的对象** - 每个线程的 Java 虚拟机栈中的局部变量表里存放的引用对象。 - 例如:方法参数、局部变量(`Object obj = new Object();` 中的 `obj`)。 ```java public void method() { Object obj = new Object(); // obj 是栈帧中的局部变量,指向堆中对象 } ``` → 只要该方法未执行完,`obj` 就是 GC Root,它引用的对象就不会被回收。 --- #### 2. **本地方法栈中 JNI 引用的对象** - Native 方法(通过 `native` 关键字调用)中引用的 Java 对象。 - JVM 需确保这些被 native 代码持有的对象不被提前回收。 > 例如:某些 NIO 或文件通道操作中,native 层持有了 DirectByteBuffer 的引用。 --- #### 3. **方法区中的类静态属性引用的对象** - 类的静态字段(static fields)所引用的对象。 ```java public class Cache { public static Map<String, Object> cache = new HashMap<>(); } ``` → `cache` 是一个静态变量,只要 `Cache.class` 存活,这个 map 就是 GC Root,其引用的对象也不会被回收。 --- #### 4. **方法区中的常量引用的对象** - 字符串常量池(String Table)中的引用(如 `String s = "hello"`)、final 常量等。 ```java String s = "Hello"; // "Hello" 在字符串常量池中,属于 GC Root ``` > 注意:JDK7+ 后字符串常量池移到了堆中,但仍然作为 GC Root 管理。 --- #### 5. **所有被同步锁(synchronized)持有的对象** - 被 `synchronized` 关键字锁定的对象 monitor 锁。 ```java synchronized (obj) { // obj 此时正在被当作锁使用,它是一个 GC Root } ``` → 在同步块执行期间,`obj` 必须存活,不能被回收。 --- #### 6. **Java 虚拟机内部引用** - 包括基本数据类型对应的 Class 对象(如 `Integer.TYPE`)、常驻异常对象(如 `NoClassDefFoundError`)、系统类加载器等。 - 这些是 JVM 自身运行所需的核心对象。 --- #### 7. **被 JVMTI(JVM Tool Interface)使用的对象** - 如调试器、性能监控工具(如 JVisualVM、Arthas)附加到 JVM 时注册的引用。 - 比如:断点设置、对象监视等可能临时持有对象引用。 --- #### 8. **被 F-Reference(Finalizer)引用的对象(特殊情况)** - 实现了 `finalize()` 方法的对象,在被回收前会进入一个 finalization 队列。 - 这些对象在 finalize 执行完成前也会被视为临时 GC Root。 > ⚠️ 不推荐依赖 `finalize()`,已被废弃(JDK9 起标记为 deprecated)。 --- ### 总结:常见 GC Roots 类型一览表 | 来源 | 示例 | |------|------| | 栈帧中的局部变量 | 方法内的对象引用 `Object obj` | | 方法区静态变量 | `public static Object instance` | | 方法区常量 | 字符串常量 `"abc"`、`static final Object CONST` | | 本地方法栈 JNI 引用 | Native 方法持有的 Java 对象 | | synchronized 锁对象 | `synchronized(obj)` 中的 `obj` | | JVM 内部对象 | Class 对象、异常类、系统类加载器 | | 工具附加引用 | Profiler、Debugger 持有的对象 | | Finalizer 队列对象 | 等待执行 `finalize()` 的对象 | --- ✅ **关键理解:** - GC Roots 是“根”,不是普通对象。 - 它们本身不会被回收(除非类卸载或线程结束)。 - 只要从 GC Roots 到某个对象存在引用链,该对象就**存活**。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值