java finalize方法

为什么写这篇文章?
要说finalize方法我想做java的都知道,那么finalize方法会不会执行,如果会什么时候执行?如果重写finalize方法又有什么严重的后果?

为什么写这篇文章?

要说finalize方法我想做java的都知道,那么finalize方法会不会执行,如果会什么时候执行?如果重写finalize方法又有什么严重的后果?

题外话:以前看Java GC相关内容主要为的是应付面试而已,不过最近有个同事提了个问题(问题你么先YY),但他只说了对象会被回收,具体细节并没有说出,进而引发我再次探究GC
估计下次会以此问题展开讨论,由于我也是个菜鸟需要大家指出文章中的不足,谢谢!

Java finalize方法

finalize方法是Object类的方法,任何类都是重写finalize方法,实现自己想要的功能。
默认的finalize方法什么也没做

1
protected void finalize() throws Throwable { }

但是一般都不建议自己重写finalize方法,由于在清理对象时候无法保证finalize方法一定会被执行。

现在我们有一段小程序非常的简单,就是打印一句话,然后程序就结束了。
那么,对象会被回收吗?

什么时候执行GC我么是不清楚的,根据不同的算法有不同的调度,有的是根据时间调度,有的是根据
内存使用的情况进行调度。

不过让我来做的话,我更倾向于后者,不管运行多长时间只要内存没到我指定的阈值大小我就不执行,
现在的这个想法来源hadoop的spill,假设有100m的内存使用但是只要达到上限80m的内存用量,
那么我就开始执行GC。【只是我的想法,哈哈~】

那好,无论是根据时间又或者是内存大小进行GC,但是我么就一段输出代码,程序结束了估计也不会
执行GC线程进行清理吧!

没有执行GC也就是无法执行到finalize方法了。

那么,有什么方法可以执行gc呢?方法是有的,但是也不能保证一定会执行gc,只能说会催促进而执行
GC。

这也就是我么常说的

1
System.gc()

当然还存在其它方法,在最后我会将我参考链接发出。
那么,什么样的对象会被执行finalize方法呢?finalize方法又会被执行多少次呢?

对象销毁过程

对象的销毁过程中,按照对象的finalize执行情况,可以分为以下几种,系统会记录对象的
对应状态。

1、unfinalized 没有执行finalize,系统也不准备执行。
2、finalizable 可以执行finalize了,系统会在随后的某个时间执行finalize。
3、finalized该对象的finalize已经被执行了。

GC怎么来保持对finalizable的对象的追踪呢。GC有一个Queue,
叫做F-Queue,所有对象在变为finalizable的时候会加入到该Queue,然后等待GC执行它的
finalize方法。

这时我们引入了对对象的另外一种记录分类,系统可以检查到一个对象属于哪一种。

a.reachable:活动的对象引用链可以到达的对象,包括所有线程当前栈的局部变量,
所有的静态变量等等。

b.finalizer-reachable除了reachable外,从F-Queue可以通过引用到达的对象。
c.unreachable其它的对象

转换过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1.首先,所有的对象都是从Reachable+Unfinalized走向死亡之路的。
2.当前活动对象不可达时,对象可以从Reachable状态变到F-Reachable或者Unreachable状态。
3.当对象为非Reachable+Unfinalized时,GC会把它移入F-Queue,
状态变为F-Reachable+Finalizable。
4.好了,关键的来了,任何时候,GC都可以从F-Queue中拿到一个Finalizable的对象,
标记它为Finalized,然后执行它的finalize方法,由于该对象在这个线程中又可达了,
于是该对象变成Reachable了(并且Finalized)。而finalize方法执行时,又有可能把其它的F-Reachable的对象变为一个Reachable的,这个叫做对象再生。
5.当一个对象在Unreachable+Unfinalized时,如果该对象使用的是默认的Object的finalize,
或者虽然重写了,但是新的实现什么也不干。为了性能,GC可以把该对象直接变到Reclaimed状态直接销毁,而不用加入到F-Queue等待GC做进一步处理。
6.从状态图看出,不管怎么折腾,任意一个对象的finalize只至多执行一次,一旦对象变为Finalized
就怎么也不会在回到F-Queue去了。当然没有机会再执行finalize了。
7.当对象处于Unreachable+Finalized时,该对象离真正的死亡不远了。GC可以安全的回收该对象的
内存了。进入Reclaimed。

实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test1 {
Test2 t2 ;
public Test1(Test2 t2) {
this.t2 = t2;
}
@Override
protected void finalize() throws Throwable {
System.out.println("Test1 finalize...");
Test3.t1 = this;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test2 {
String name;
int age;
public Test2(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected void finalize() throws Throwable {
System.out.println("Test2 finalize...");
}
@Override
public String toString() {
return this.name + "is " + age;
}
}
1
2
3
4
5
6
public class Test3 {
static Test1 t1;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {
public static void main(String[] args) throws InterruptedException {
Test1 t1 = new Test1(new Test2("joker", 18));
System.out.println(t1);
t1 = null;
System.gc();
Thread.sleep(10000);
System.out.println(Test3.t1);
System.out.println(Test3.t1.t2);
t1 = null;
System.gc();
System.out.println("done.");
}
}

输出如下:

1
2
3
4
5
6
7
8
9
10
cn.base.gc.test.Test1@6b04d3c8
[GC 2642K->437K(251392K), 0.0012310 secs]
[Full GC 437K->350K(251392K), 0.0102620 secs]
Test1 finalize...
Test2 finalize...
cn.base.gc.test.Test1@6b04d3c8
jokeris 18
[GC 2992K->446K(251392K), 0.0006800 secs]
[Full GC 446K->350K(251392K), 0.0069980 secs]
done.

可以看到的是我们在释放test1的时候成员对象test2也一起被回收了,由于test1重写了finalize
方法,在最后test1又复活了。

由于在GC Root中又有引用链起死回生,但是我么再一次设置null并执行gc可以看到test1对象
没有在进入finalize方法了。

总结

finalize方法不是每次都会执行的,使用System.gc()
也只不过是加快gc调用,并且重写finalize方法最好不要使对象再生,这样容易造成
对象的生命周期混乱!

参考

http://mazhuang.org/2015/12/15/java-object-finalize/
http://bijian1013.iteye.com/blog/2289661

人生苦短,我要打赏!