# 15. Java Finalize 方法的作用是什么?为什么不推荐使用?

# 标准答案

finalize() 方法是 Java 对象的终结(finalization)机制,在对象被 GC 之前由 JVM 进行回调,通常用于释放非 Java 资源(如文件句柄、网络连接)。但 finalize() 存在不确定性、性能开销大、容易导致内存泄漏,因此 不推荐使用。JDK 9 之后已被废弃,JDK 18 彻底移除。推荐使用 try-with-resources(AutoCloseable)或显式 close() 方法 进行资源管理。

# 答案解析

# finalize() 的作用

finalize()Object 类的一个方法,子类可重写它,在对象被垃圾回收前执行清理逻辑:

@Override
protected void finalize() throws Throwable {
    System.out.println("Finalizing...");
}
1
2
3
4

但 JVM 何时调用 finalize() 不可控,它不会立即释放资源,因此不是可靠的清理机制。

# finalize() 存在的问题

  1. 不确定性:GC 运行不可预测,finalize() 可能长时间不执行,甚至永远不执行。
  2. 影响 GC 性能:JVM 需要专门的 Finalizer 线程管理 finalize() 队列,拖慢 GC 速度。
  3. 可能导致对象复活:在 finalize() 内部如果将 this 赋值给静态变量,对象会 重新变成可达,影响 GC。
  4. 已被废弃:JDK 9 标记为 @Deprecated,JDK 18 彻底移除。

示例:finalize() 造成对象复活

public class FinalizerEscape {
    static FinalizerEscape instance;

    @Override
    protected void finalize() throws Throwable {
        instance = this; // 复活对象
    }

    public static void main(String[] args) throws InterruptedException {
        instance = new FinalizerEscape();
        instance = null;
        System.gc(); // 触发 GC
        Thread.sleep(1000);
        System.out.println(instance != null); // true,对象复活
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 更好的替代方案

  1. 使用 try-with-resourcesAutoCloseable 机制):
    class Resource implements AutoCloseable {
        @Override
        public void close() {
            System.out.println("Resource closed");
        }
    }
    
    try (Resource res = new Resource()) {
        // 使用资源
    } // 自动调用 close()
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  2. 手动 close() 方法
    class CustomResource {
        void close() {
            System.out.println("Manually releasing resources");
        }
    }
    
    CustomResource res = new CustomResource();
    res.close(); // 显式释放资源
    
    1
    2
    3
    4
    5
    6
    7
    8
  3. 使用 PhantomReference 进行对象回收监控
    ReferenceQueue<Object> queue = new ReferenceQueue<>();
    PhantomReference<Object> ref = new PhantomReference<>(new Object(), queue);
    
    1
    2

# 常见错误

  1. 误以为 finalize() 是 GC 的一部分,实际它运行在 Finalizer 线程,不会加速 GC。
  2. 依赖 finalize() 释放资源,可能导致资源泄漏,正确方式是 try-with-resourcesclose()
  3. finalize() 里抛异常,不会传播,可能导致对象无法被回收。

# 最佳实践

  • 完全避免 finalize(),使用 try-with-resourcesclose()
  • 监控 Finalizer 队列,避免 GC 受影响:
    jcmd <pid> GC.finalizer_info
    
    1
  • 确保资源及时释放,避免资源泄漏和 OOM。

# 深入追问

  • 为什么 finalize() 的执行时间无法预测?
  • PhantomReference 如何替代 finalize() 进行资源管理?
  • try-with-resources 在 JDK 7 之前如何实现?

# 相关面试题

  • finalize()close()try-with-resources 的区别?
  • Java 里有哪些资源管理方式?
  • PhantomReference 的应用场景?
  • 为什么 JDK 18 移除了 finalize()