finalize()已被废弃,必须规避:Java 9起标记@Deprecated,Java 18起设forRemoval=true;应优先用try-with-resources+AutoCloseable及时释放外部资源,或用Cleaner管理堆外资源,显式close()明确责任归属。

Java 9 起,Object.finalize() 被正式标记为 @Deprecated,到 Java 18 已明确设为 forRemoval = true。它不再是一种可行的资源清理手段,而是必须规避的技术包袱。替代方式不是“选一个差不多的”,而是按场景明确分工、各司其职。
优先用 try-with-resources + AutoCloseable
适用于所有需要及时释放的外部资源:文件流、数据库连接、网络 Socket、内存映射文件等。 核心是让资源类实现 `AutoCloseable` 接口,并在 `close()` 方法里完成清理逻辑。JVM 会在 `try` 块退出时**确定性调用** `close()`,不依赖 GC、不延迟、可监控、可加超时或重试。 - 不要等对象被回收才关资源,而是在业务逻辑结束那一刻就释放 - `try-with-resources` 会自动处理异常压制(suppressed exceptions),比手写 `finally` 更安全 - 示例: ```java try (FileInputStream fis = new FileInputStream("data.txt"); BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) { // 使用资源 } // ← 这里 fis 和 reader 的 close() 一定被执行 ```需要用 Cleaner 管理非堆资源
适合清理 JVM 堆外资源:如 `DirectByteBuffer` 占用的本地内存、`MappedByteBuffer` 映射的文件、JNI 分配的 native 内存等。 `Cleaner` 基于虚引用(`PhantomReference`)和独立线程,对象不可达后异步执行清理动作,**不阻塞 GC、不支持对象复活、不拖慢回收周期**。 - `Cleaner` 实例建议声明为 `static`,避免自身成为内存泄漏源 - 清理动作应尽量轻量、无阻塞、不抛异常(或至少捕获并忽略) - 示例: ```java private static final Cleaner cleaner = Cleaner.create(); private final Cleaner.Cleanable cleanable; private final ByteBuffer directBuf;public MyBuffer(int size) { this.directBuf = ByteBuffer.allocateDirect(size); this.cleanable = cleaner.register(this, () -> { if (directBuf.isDirect()) { CleanerImpl.clean(directBuf); // 或调用相关清理逻辑 } }); }
<H3>显式 close() 是最直接的责任归属</H3> 当资源生命周期由业务控制(比如连接池中的连接、自定义服务实例),应在明确的退出点调用 `close()` 或 `shutdown()`。 - 把资源释放当作业务逻辑的一部分,而不是“交给 GC 处理” - 配合单元测试验证 `close()` 是否被调用(例如用 Mockito 检查方法调用) - 若资源可能被多次使用,需设计状态机防止重复关闭或已关闭状态下再操作 <H3>慎用 ShutdownHook 作兜底</H3> 仅限 JVM 正常退出前的最后补救,比如刷新缓存、记录统计、关闭守护线程。 - 它不保证执行(如 kill -9 或 crash 时不会触发) - 不能替代常规资源管理,也不能用于释放关键句柄(文件、端口等可能已被 OS 回收) - 若必须使用,动作要极简、无依赖、不阻塞 本质上,`finalize` 的退出不是功能缺失,而是工程理念升级:资源生命周期必须主动管理,而非被动等待。


















