Java 对象释放与 finalize 方法

转载 2016年06月01日 13:16:19

(转载)https://mazhuang.org/2015/12/15/java-object-finalize/

本文谈论的知识很浅显,只是我发现自己掌握的相关知识并不扎实,对细节并不清楚,遂将疑惑解开,并记录于此。

按惯例先上结论,对如下知识点已经清楚的选手可以省下看本文的时间了。

结论

  1. 对象的 finalize 方法不一定会被调用,即使是进程退出前。

  2. 发生 GC 时一个对象的内存是否释放取决于是否存在该对象的引用,如果该对象包含对象成员,那对象成员也遵循本条。

  3. 对象里包含的对象成员按声明顺序进行释放。

证明

假设有以下类定义:

class A {
    public A() {
        System.out.println("A()");
    }

    protected void finalize() {
        System.out.println("~A()");
    }

    B b;
}

class B {
    public B() {
        System.out.println("B()");
    }

    protected void finalize() {
        System.out.println("~B()");
    }
}

结论 1 证明

在 main 方法中有如下代码:

A a = new A();
B b = new B();
a.b = b;
a = null;

输出是什么呢?

A()
B()

与我想象中的有些不一样,我以为至少在进程退出前 A 类对象和 B 类对象都会被释放掉的。

我们明确一下 finalize 方法的调用时机,引用官方 API 文档的解释:

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.

也就是说,finalize 是在 JVM 执行 GC 的时候才会执行的,而很显然,在这个例子里 main 方法退出时并没有执行 GC,而 GC 是否执行以及其执行的时机并不是我们可以精确控制的,此即证明了结论 1

结论 2 证明

虽然我们不能精确控制 GC 的时机,但我们可以给 JVM 建议,比如我们在最后加个 System.gc() 建议 JVM 进行 GC。

A a = new A();
B b = new B();
a.b = b;
a = null;
System.gc();

现在输出变成了

A()
B()
~A()

可见 JVM 听从了我们的建议,执行了 GC,由于此时 A 类对象已经没有引用了,所以它被释放,而该对象的 B 类对象成员由于被局部变量 b 引用,此时不会释放。

而一个在 GC 时对象成员也会被释放的 A 类对象调用是怎么样的呢?

A a = new A();
a.b = new B();
a = null;
System.gc();

此时输出为

A()
B()
~B()
~A()

如上两段代码执行结果的对比证明了结论 2

另外需要说明的是,Runtime 类里有一个 runFinalizersOnExit 方法,可以让程序在退出时执行所有对象的未被自动调用 finalize 方法,即使该对象仍被引用。但是从官方文档可以看出,该方法已经废弃,不建议使用,引用官方 API 文档如下:

Deprecated. This method is inherently unsafe. It may result in finalizers being called on live objects while other threads are concurrently manipulating those objects, resulting in erratic behavior or deadlock.

Enable or disable finalization on exit; doing so specifies that the finalizers of all objects that have finalizers that have not yet been automatically invoked are to be run before the Java runtime exits. By default, finalization on exit is disabled.

而同样是 Runtime 类里的 runFinalization 方法则在调用后并没有看到明显的效果,即如果不发生 GC,那即使调用了 runFinalization 方法,已经待回收的对象的 finalize 方法依然没有被调用。

结论 3 证明

我们修改一下几个类的定义:

class A {
    public A() {
        System.out.println("A()");
    }

    protected void finalize() {
        System.out.println("~A()");
    }

    B b;    // line a
    C c;    // line b
}

class B {
    public B() {
        System.out.println("B()");
    }

    protected void finalize() {
        System.out.println("~B()");
    }
}

class C {
    public C() {
        System.out.println("C()");
    }

    protected void finalize() {
        System.out.println("~C()");
    }
}

现在在 main 方法里有如下调用:

A a = new A();
a.b = new B();
a.c = new C();
a = null;
System.gc();

输出是

A()
B()
C()
~B()
~C()
~A()

而如果我们互换一下 A 类声明带注释的 line a 与 line b 的位置,即变成

C c;    // line b
B b;    // line a

输出变成

A()
B()
C()
~C()
~B()
~A()

此即证明了结论 3

JVM内存回收之finalize()方法

finalize()方法        之所以要使用finalize(),是存在着垃圾回收器不能处理的特殊情况。假定你的对象(并非使用new方法)获得了一块“特殊”的内存区域,由于垃圾回收器只...
  • u013366812
  • u013366812
  • 2016年07月27日 16:28
  • 2070

慎用Finalize方法以及Finalize方法的妙用

首先,要知道finalize方法跟c++的析构函数不一样,jvm并不保证会执行。 一、慎用finalize方法 finalize方法在什么时候被调用: 在垃圾回收的时候,某个对象要被回收的时候,会...
  • q291611265
  • q291611265
  • 2015年07月29日 20:14
  • 2231

【Effective Java】Ch2_创建销毁对象:Item7_避免使用finalize方法

Finalizer通常是不可预测的、危险的、不必要的。使用finalizer会导致不稳定的行为、低下的性能、以及可移植问题。Finalizer也有其可用之处,本文稍后会做介绍,但是作为一个首要法则,你...
  • vking_wang
  • vking_wang
  • 2013年02月19日 17:56
  • 1972

JAVA中对象什么时候死亡以及什么时候执行finalize()方法

一、可达性算法   要知道对象什么时候死亡,我们需要先知道JVM的GC是如何判断对象是可以回收的。JAVA是通过可达性算法来来判断对象是否存活的。这个算法的基本思路就是通过一系列的称为“GC R...
  • amaxiaochen
  • amaxiaochen
  • 2017年10月24日 11:18
  • 130

在finalize方法中复活java对象

/** * 该类用于演示在对象的finalize()方法中复活对象(来自《实战java虚拟机》-葛一鸣) * 2017年4月4日 下午9:06:00 * @version v1.0 */ pu...
  • nmgrd
  • nmgrd
  • 2017年04月04日 21:09
  • 513

7. 【创建和销毁对象】避免使用终结方法finalize

终结方法(finalize)通常是不可预测的,也是很危险的,因此一般情况下应该尽量避免使用终结方法。...
  • get_set
  • get_set
  • 2016年05月03日 23:19
  • 573

从头认识java-4.5 对象的清理(finalize())

这一章节我们来简单讨论一下对象的清理。注意:在c++里面会提供析构函数,来清除对象,但是在java中,对象是被垃圾回收器回收的。回收不一定被析构,因为回收了对象,只有当回收器超过某个限度的内存是,才发...
  • raylee2007
  • raylee2007
  • 2015年10月26日 14:27
  • 2904

Java中的“析构函数”——finalize() 对象消亡时调用

《JAVA编程思想》: java提供finalize()方法,垃圾回收器准备释放内存的时候,会先调用finalize()。       (1).对象不一定会被回收。        (...
  • jemasw
  • jemasw
  • 2013年01月05日 19:37
  • 20876

JAVA编程思想(第4版)对象终结条件,system.gc(),finalize()一部分用法小结

finalize()有一个有趣的用法,它并不依赖于每次都要对finalize()进行调用,这就是对象终结条件的验证。 当对某个对象不再感兴趣----也就是它可以被清理了,这个对象应该处于某种状态,使...
  • u014621130
  • u014621130
  • 2015年04月05日 14:34
  • 792

C#中对象的销毁有三种方式Finalize,Dispose,GC。请大虾们描述三种方式的区别啊?/java中的析构函数

MSDN建议按照下面的模式实现IDisposable接口: 1 public class Foo: IDisposable 2 { 3 public void Dispose() 4 ...
  • zzy7075
  • zzy7075
  • 2016年06月24日 08:45
  • 1768
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java 对象释放与 finalize 方法
举报原因:
原因补充:

(最多只允许输入30个字)