首先我们来了解一下finalize方法,finalize由对象上的垃圾回收器在垃圾收集确定没有更多对该对象的引用(GC root不可达)时调用。子类重写finalize方法来处置系统资源或执行其他清理工作。
finalize
protected void finalize ()
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方法由名为“Finalizer”的守护线程执行,只会被调用一次
- 在finalize方法中,可将待回收对象赋值给GC Root从而达到对象再生的目的
之后我们了解下GC Root,GC Root即Garbage collection root,Android开发者官网GC Root的阐述如下:
There are several kinds of garbage collection roots in Java:
- references on the stack
- Java Native Interface (JNI) native objects and memory
- static variables and functions
- threads and objects that can be referenced
- classes loaded by the bootstrap loader
- finalizers and unfinalized objects
- busy monitor objects
在JAVA中有如下几种GC Root:
- 在栈上的引用
- JNI本地对象和内存
- 静态变量和函数
- 线程和可以被引用的对象
- 被bootstrap loader加载的类
- finalizers和unfinalized的对象
- 被占用的监视器对象
我们知道GC对垃圾对象的判断依据是对象是否从GC Root可达,结合finalize方法的API解释,可知对象不可达时会调用对象的finalize方法。接下来我们用代码来验证一下上述几种GC Root:
一、静态变量
public class RootTest {
static int gcTime = 0;
//静态变量
static RootTest rootTest = null;
public static void main(String[] args) throws InterruptedException {
rootTest = new RootTest();
gc();
Thread.sleep(1000);
gcTest();
gcTest();
}
static void gcTest() throws InterruptedException {
rootTest = null;
gc();
Thread.sleep(1000);
if (rootTest == null)
System.out.println("rootTest dead");
else
System.out.println("rootTest alive");
}
static void gc() {
System.gc();
System.out.println("gc " + ++gcTime);
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("RootTest finalize() " + Thread.currentThread().getName());
rootTest = this;
}
}
我们使用了静态变量rootTest引用RootTest对象后立即执行一次gc,然后执行两次gcTest:
- 将rootTest置为null
- 强制执行gc
- 休息1s等Finalizer线程执行finalize方法
- 判断rootTest状态
运行结果:
gc 1
gc 2
RootTest finalize() Finalizer
rootTest alive
gc 3
rootTest dead
可见,第一次执行gc后,GC发现RootTest对象是可达的,RootTest对象的finalze方法没有被调用。
第二次将rootTest置为null后,执行gc时GC发现RootTest对象不可达,RootTest对象的finalze方法被调用,finalze中RootTest对象再生。
第三次将rootTest置为null后,执行gc时GC发现RootTest对象不可达,RootTest对象的finalze方法被调用过了,这次不会再被调用,RootTest对象被回收。
二、线程、栈上的引用
public class RootTest {
static int gcTime = 0;
public static void main(String[] args) throws InterruptedException {
//栈上的引用
Thread thread1 = new Thread() {
@Override
public void run() {
System.out.println("Thread1 run over");
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Thread1 finalize() " + Thread.currentThread().getName());
}
};
thread1.start();
new Thread() {
@Override
public void run() {
System.out.println("Thread2 run over");
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Thread2 finalize() " + Thread.currentThread().getName());
}
}.start();
new Thread() {
@Override
public void run() {
int x = 0;
while (x < 3) {
System.out.println("Thread3 run " + ++x);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Thread3 run over");
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Thread3 finalize() " + Thread.currentThread().getName());
}
}.start();
gc();
Thread.sleep(5000);
gc();
new Thread() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
gc();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
static void gc() {
System.gc();
System.out.println("gc " + ++gcTime);
}
}
在main方法中起三个子线程,第一个执行打印后立即结束运行但是会被栈中的局部变量引用,第二个执行打印后立即结束运行,第三个会循环三次打印,接着执行三次gc
运行结果:
Thread1 run over
Thread2 run over
Thread3 run 1
gc 1
Thread2 finalize() Finalizer
Thread3 run 2
Thread3 run 3
Thread3 run over
gc 2
Thread3 finalize() Finalizer
gc 3
Thread1 finalize() Finalizer
首先子线程Thread1运行结束,Thread2运行结束,Thread3开始执行
然后,在Thread3运行期间第一次执行gc,可以看到Thread2的finalze方法被调用
然后,在Thread3运行结束后第二次执行gc,可以看到Thread3的finalze方法被调用
然后,当main方法运行结束后第三次执行gc,可以看到Thread1的finalze方法被调用
三、被占用的监视器对象
public class RootTest {
static int gcTime = 0;
public static void main(String[] args) throws InterruptedException {
new UnusedLock();
new Thread() {
@Override
public void run() {
int x = 0;
synchronized (new UsedLock()) {
while (x < 3) {
System.out.println("Thread run " + ++x);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Thread run over");
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Thread finalize() " + Thread.currentThread().getName());
}
}.start();
gc();
Thread.sleep(5000);
gc();
Thread.sleep(5000);
}
static void gc() {
System.out.println("gc start " + ++gcTime);
System.gc();
System.out.println("gc over " + gcTime);
}
}
class UsedLock {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("UsedLock finalize()");
}
}
class UnusedLock {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("UnusedLock finalize()");
}
}
直接new一个不会被占用的监视器对象,然后启动一个循环打印三次的子线程,并在其中使用一个被其占用的监视器对象,接着执行两次gc
运行结果:
gc start 1
Thread run 1
gc over 1
UnusedLock finalize()
Thread run 2
Thread run 3
Thread run over
gc start 2
gc over 2
UsedLock finalize()
Thread3 finalize() Finalizer
首先,在Thread3执行期间第一次执行gc,可以看到UnusedLock的finalze方法被调用
然后,当Thread3执行完成后第二次执行gc,可以看到UsedLock和Thread的finalize方法都被调用了