原文:https://www.cnblogs.com/xiaoxi/p/6486852.html
对于可达性分析算法而言,未到达的对象并非是“非死不可”的,若要宣判一个对象死亡,至少需要经历两次标记阶段。
1. 如果对象在进行可达性分析后发现没有与GCRoots相连的引用链,则该对象被第一次标记并进行一次筛选,筛选条件为是否有必要执行该对象的finalize方法(问题执行finalize方法有什么影响?),若对象没有覆盖finalize方法或者该finalize方法是否已经被虚拟机执行过了,则均视作不必要执行该对象的finalize方法,即该对象将会被回收。反之,若对象覆盖了finalize方法并且该finalize方法并没有被执行过,那么,这个对象会被放置在一个叫F-Queue的队列中,之后会由虚拟机自动建立的、优先级低的Finalizer线程去执行,而虚拟机不必要等待该线程执行结束,即虚拟机只负责建立线程,其他的事情交给此线程去处理。
2.对F-Queue中对象进行第二次标记,如果对象在finalize方法中拯救了自己,即关联上了GCRoots引用链,如把this关键字赋值给其他变量,那么在第二次标记的时候该对象将从“即将回收”的集合中移除,如果对象还是没有拯救自己,那就会被回收(也就是判断有没有关联上GCROOTS 如果第一次没有关联上GCroot 那么就放到F-quque的队列中,然后由虚拟机的自动建立的Finalizer线程来进行执行,如果这个线程中对象还是没有关联上GC root 则将他回收。)。如下代码演示了一个对象如何在finalize方法中拯救了自己,然而,它只能拯救自己一次,第二次就被回收了。具体代码如下:
package com.demo;
/*
* 此代码演示了两点:
* 1.对象可以再被GC时自我拯救
* 2.这种自救的机会只有一次,因为一个对象的finalize()方法最多只会被系统自动调用一次
* */
public class FinalizeEscapeGC {
public String name;
public static FinalizeEscapeGC SAVE_HOOK = null;public FinalizeEscapeGC(String name) {
this.name = name;
}public void isAlive() {
System.out.println("yes, i am still alive :)");
}
@Override
protected void finalize() throws Throwable {
super.finalize(); //执行finalize 方法
System.out.println("finalize method executed!");
System.out.println(this);
FinalizeEscapeGC.SAVE_HOOK = this;
}@Override
public String toString() {
return name;
}public static void main(String[] args) throws InterruptedException {
SAVE_HOOK = new FinalizeEscapeGC("leesf");
System.out.println(SAVE_HOOK);
// 对象第一次拯救自己
SAVE_HOOK = null;
System.out.println(SAVE_HOOK);
System.gc();
// 因为finalize方法优先级很低,所以暂停0.5秒以等待它
Thread.sleep(500);
if (SAVE_HOOK != null) {
SAVE_HOOK.isAlive();
} else {
System.out.println("no, i am dead : (");
}// 下面这段代码与上面的完全相同,但是这一次自救却失败了
// 一个对象的finalize方法只会被调用一次
SAVE_HOOK = null;
System.gc();
// 因为finalize方法优先级很低,所以暂停0.5秒以等待它
Thread.sleep(500);
if (SAVE_HOOK != null) {
SAVE_HOOK.isAlive();
} else {
System.out.println("no, i am dead : (");
}
}
}
运行结果:
leesf
null
finalize method executed!
leesf
yes, i am still alive :)
no, i am dead : (
那么来看一下finalize()这个函数(这个得闹懂):
首先 finalize 就是object 类中的 :
protected void finalize() throws Throwable { }
关于finalize方法,又是一个大长篇传送门:https://blog.csdn.net/pmdream/article/details/8657152
从结果我们可以得知,一个堆对象的this(放在局部变量表中的第一项)引用会永远存在,在方法体内可以将this引用赋值给其他变量,这样堆中对象就可以被其他变量所引用,即不会被回收。
四:方法区的垃圾回收
方法区得垃圾回收:1.废弃常量 2.无用的类
那么问题就是在于什么时候判断什么是废弃的常量,什么是无用的类。
判断: 字面量回收为例,如果abc 进入了常量池,但是当前系统没有任何一个String对象
引用了这个字面量(什么是字面量?)如果发生垃圾回收并且有必要的时候,abc就会被移除。
如何判断无用的类呢?需要满足以下三个条件
1.Java堆中不存在该类的任何实例。
2. 加载该类的ClassLoader已经被回收。
3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。