GC(Garbage Collecor)是JVM的内存回收器,当应用使用的内存不足时,会导致OOM(Out-Of-Memory)。
Java提供的GC可以自动监测对象是否超过作用域从而达到自动回收内存的目的(Java没有提供主动释放已分配内存的方法)。
Java的GC会自动管理内存,如果要主动请求内存回收,可以调用以下方法:
当我们创建对象时,JVM就会为我们创建的对象分配一块内存,那么这块内存GC如何管理呢?
引用计数法(该算法无法解决循环引用的情况,导致内存无法释放,GC已不使用该方法):
对象创建时,初始化计数为1; 每当有一个地方引用它,计数就+1; 每当有一个地方引用失效时,计数就-1;
可达性分析法(GC目前彩该方法):
该方法的基本思想是通过一系列称为“GC Roots”的对象作为起点,从这些点向下搜索,搜索走过的路径称为“引用链”,当某个对象到GC Roots没有任何引用链(即GC Roots不可达至该对象)时,则证明该对象是不可用的,就可以回收该对象内存。
其它算法(会在以后分别分析)
如何选择GC Roots呢?在Java中,可以作为GC Roots的包括以下几种:
下面给出一个GC Roots的图例:
gc roots.png
上图为GC Roots的引用链,obj8, obj9, obj10都没有到GC Roots对象的引用链,因此会被回收。
references.png
对于可达性分析算法而言,未到达的对象并非是“非死不可”的,若要宣判一个对象的死亡,至少需要经历两次标记阶段:
是否有必要执行该对象的finalize方法
以下代码演示了对象如何在finalize方法中拯救自己(然后,它只能自救一次,第二次仍旧被回收):
public class GC {
public static GC SAVE_HOOK = null;
public static void main(String[] args) throws InterruptedException {
// 新建对象,因为SAVE_HOOK指向这个对象,对象此时的状态是(reachable,unfinalized)
SAVE_HOOK = new GC();
// 将SAVE_HOOK设置成null,此时刚才创建的对象就不可达了,因为没有句柄再指向它了,
// 对象此时状态是(unreachable,unfinalized)
SAVE_HOOK = null;
// 强制系统执行垃圾回收,系统发现刚才创建的对象处于unreachable状态,
// 并检测到这个对象的类覆盖了finalize方法,因此把这个对象放入F-Queue队列,
// 由低优先级线程执行它的finalize方法,此时对象的状态变成(unreachable, finalizable)
// 或者是(finalizer-reachable,finalizable)
System.gc();
// sleep,目的是给低优先级线程从F-Queue队列取出对象并执行其finalize方法提供机会。
// 在执行完对象的finalize方法中的super.finalize()时,对象的状态变成(unreachable,finalized)状态,
// 但接下来在finalize方法中又执行了SAVE_HOOK = this;这句话,又有句柄指向这个对象了,对象又可达了。
// 因此对象的状态又变成了(reachable, finalized)状态。
Thread.sleep(500);
// 这里楼主说对象处于(reachable,finalized)状态应该是合理的。对象的finalized方法被执行了,
// 因此是finalized状态。又因为在finalize方法是执行了SAVE_HOOK=this这句话,本来是unreachable的对象,
// 又变成reachable了。
if (null != SAVE_HOOK) { //此时对象应该处于(reachable, finalized)状态
// 这句话会输出,注意对象由unreachable,经过finalize复活了。
System.out.println("Yes , I am still alive");
} else {
System.out.println("No , I am dead");
}
// 再一次将SAVE_HOOK放空,此时刚才复活的对象,状态变成(unreachable,finalized)
SAVE_HOOK = null;
// 再一次强制系统回收垃圾,此时系统发现对象不可达,虽然覆盖了finalize方法,但已经执行过了,因此直接回收。
System.gc();
// 为系统回收垃圾提供机会
Thread.sleep(500);
if (null != SAVE_HOOK) {
// 这句话不会输出,因为对象已经彻底消失了。
System.out.println("Yes , I am still alive");
} else {
System.out.println("No , I am dead");
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize method executed!");
// 这句话让对象的状态由unreachable变成reachable,就是对象复活
SAVE_HOOK = this;
}
}
运行结果如下:
finalize method executed!
yes, i am still alive
no, i am dead
由此可见,该对象只能拯救自己一次,因为对象的finalize方法最多被JVM调用一次。
此外,我们还可以得知,一个堆对象的this(放在局部变量表中的第一项)引用会永远存在,在方法体内可以将this引用赋值给其它变量,这样堆中对象就可以被其它变量所引用,即不会被回收。
既然进行垃圾回收,就需要判断哪些是废弃常量,哪些是无用的类?
如何判断废弃常量呢?以字面量回收为例,如果一个字符串“abc”已经进入常量池,但是当前系统没有任何一个String对象引用了叫做“abc”的字面量,那么,如果发生垃圾回收并且有必要时,“abc”就会被系统移出常量池。常量池中的其他类(接口)、方法、字段的符号引用也与此类似。
如何判断无用的类呢?需要满足以下三个条件:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。