目录

裴先生
裴先生
发布于 2020-10-10 / 3 阅读
0
0

Java 内存管理:深入理解 finalize() 与垃圾回收机制

原创

1. 概述

在《Thinking in Java》第五章 5.5 小节“清理:终结处理和垃圾回收”中,作者强调了对象清理工作的重要性。虽然 Java 有垃圾回收器(GC)处理 new 出来的内存,但对于“特殊内存区域”或特定的状态校验,仍需深入理解其内部机制。

2. finalize() 方法

垃圾回收器只知道释放通过 new 开辟的内存。针对特殊的内存空间,Java 提供了 finalize() 方法。

工作原理

  1. 一旦垃圾回收器准备好释放对象占用的内存,首先调用其 finalize() 方法。
  2. 下一次垃圾回收动作发生时,才会真正清理对象占用的内存。
  3. finalize() 是基类 Object 的方法,特殊场景下需重写。

核心注意事项

  • 对象可能不被垃圾回收:如果 JVM 内存充足,可能永远不会触发 GC。
  • 垃圾回收不等于“析构”:Java 不保证 finalize() 一定执行,这与 C++ 的析构函数有本质区别。
  • 垃圾回收只与内存有关:它的唯一目的是回收不再使用的内存资源。

3. finalize() 的特殊用法:终结条件验证

finalize() 并不适合作为通用的清理工具,但它可以用来验证对象的终结状态。例如,一个对象在被销毁前必须处于某种状态(如“已关闭”或“已清空”)。

示例代码:水箱(Tank)状态检查

/**
 * @author plm
 * @create 2021/2/16 22:40
 */
public class Tank {
    private String status; // 状态可以是 empty 或者 full

    public Tank() {}

    public Tank(String status) {
        this.status = status;
    }

    void checkIn() {
        this.status = "empty";
    }

    @Override
    protected void finalize() throws Throwable {
        if ("full".equals(status)) {
            System.out.println("Error: Tank is full. Cleanup not performed correctly!");
        }
        super.finalize();
    }
}

class Test {
    public static void main(String[] args) {
        // 正常流程:状态被重置为 empty
        Tank t = new Tank("full");
        t.checkIn(); 

        // 错误流程:创建一个 full 的 Tank 且没有 checkIn 就丢失了引用
        new Tank("full"); 

        // 强制垃圾回收器运行,尝试清理对象
        System.gc(); 
    }
}

运行结果:
Error: Tank is full. Cleanup not performed correctly!


4. 垃圾回收器算法

Java 虚拟机采用多种策略来管理内存。

4.1 引用计数 (Reference Counting)

  • 定义:每个对象含有一个计数器。引用连接时 +1,失效时 -1。计数为 0 时释放。
  • 缺陷:难以处理循环引用(对象间互相引用导致计数永不为 0)。
  • 现状:并未应用于主流 Java 虚拟机中。

4.2 自适应技术 (Adaptive GC)

现代 JVM 会根据内存状况在不同的模式间切换:

1. 停止-复制 (Stop-and-Copy)

  • 原理:暂停程序,将所有存活对象从当前堆复制到另一个堆,使内存紧凑排列。
  • 缺点:需要双倍空间;当垃圾很少时,全量复制非常浪费。

2. 标记-清扫 (Mark-and-Sweep)

  • 原理:从堆栈和静态存储区开始遍历,标记所有存活对象,然后统一清理未标记的对象。
  • 缺点:清理后的内存空间不连续(碎片化),需要后续整理。

3. 自适应 (Adaptive)

JVM 会监视回收效率:

  • 如果所有对象都很稳定,效率降低,则切换到标记-清扫模式。
  • 如果内存碎片过多,则切换回停止-复制模式。

5. 性能优化:即时编译器 (JIT)

JIT 编译器能将 Java 字节码翻译成本地机器码,显著提升运行速度:

  • 全量编译:花费时间长,增加代码长度。
  • 惰性评估 (Lazy Evaluation):如 Java HotSpot 技术。只在必要时编译代码,且执行次数越多,优化程度越高(热点代码探测)。从不执行的代码则不会被 JIT 编译。

原创

版权声明:本博客原创文章,由 裴先生 2020年10月10日 发表。
转载说明:除特殊说明外本站文章皆由 CC BY-NC-SA 4.0 协议发布,转载须注明出处。


评论