是否活着
在堆里存放着的几乎所有的对象实例,垃圾收集器在堆进行回收前,第一件事情就是要确定这些对象之中哪些还"活着",哪些已经"死去"。
引用计数算法
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器就减一;任何时刻计数器为零的对象就是不可能再被使用的。
可观来说,引用计数算法虽然占用了一些额外的内存空间来进行计数,但它的原理简单,判定效率也很高,在大多数情况下它都是一个不错的算法。但是,在Java领域至少主流的Java虚拟机里面都没有选用引用计数算法进行内存管理,主要原因是这个看似简单有很多例外情况要考虑,必须要配合大量额外处理才能保证正确地工作,譬如单纯的引用计数就很难解决对象之间相互循环引用的问题。
- 手动解除:很好理解,就是在合适的实际,解除引用关系
- 使用弱引用weakref,weakref是Python提供的标准库,旨在解决循环引用。
可达性分析算法
当前主流的商用程序语言的内存管理系统都是通过可达性分析算法来判定对象是否存活。

GC Roots
固定可作为GC Roots的对象包括以下几种:
- 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
- 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量
- 在方法区中常量引用的对象,譬如字符串常量池(StringTable)里的引用
- 在本地方法栈中JNI(即通常所说的Native方法)引用的对象
- Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NullPointException、OutOfMemoryError)等,还有系统类加载器
- 所有被同步锁(synchronized关键字)持有的对象
- 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等
对象的finalization机制
Java语言提供了对象终止(finalization)机制来允许开发人员提供对象被销毁之前的自定义处理逻辑。
finalize()
方法允许在子类中被重写,用于在对象被回收时进行资源释放。通常在这个方法中进行一些资源释放和清理工作,比如关闭文件、套接字和数据库连接等。永远不要主动调用某个对象的
finalize()
方法,应该交给垃圾回收机制调用。理由如下:- 在finalize()方法调用时可能会导致对象复活
- finalize()方法的执行时间是没有保障的,它完全由GC线程决定,极端情况下,若不发生GC,则finalize()方法将没有执行机会
- 一个糟糕的finalize()会严重影响GC的性能
生存还是死亡?
如果从所有的根节点都无法访问到某个对象,说明对象已经不再使用了。一般来说,此对象需要被回收,但事实上,也并非是"非死不可"的,这时候它们暂时处于"缓刑"阶段。一个无法触及的对象有可能在某一个条件下"复活"自己,如果这样,那么对它的回收就是不合理的,为此,定义虚拟机中的对象可能的三种状态:
- 可触及的:从根节点开始,可以到达这个对象
- 可复活的:对象的所有引用都被释放,但是对象有可能在finalize()中复活
- 不可触及的:对象的finalize()被调用,并且没有复活,那么就会进入不可触及状态。不可触及的对象不可能被复活,因为finalize()只会被调用一次。
判断一个对象ObjA是否可回收,至少要经历两次标记过程:
- 如果对象ObjA到GC Roots没有引用链,则进行第一次标记
进行筛选,判断对象是否有必要执行finalize()方法:
- 如果对象ObjA没有重写finalize()方法,或者finalize()方法已经被虚拟机调用过,则虚拟机视为"没有必要执行",ObjA被判定为不可触及的
- 如果对象ObjA重写了finalize()方法,且还未执行过,那么ObjA会被插入到F-Queue队列中,由一个虚拟机自动创建的、低优先级的Finalizer线程触发其finalize()方法执行
- finalize()方法是对象逃脱死亡的最后机会,稍后GC会对F-Queue队列中的对象进行第二次标记,如果ObjA在finalize()方法中与引用链上的任何一个对象建立了联系,那么在第二次标记时,ObjA会被移出"即将回收"集合。之后,对象会再次出现没有引用存在的情况。在这个情况下,finalize方法不会被再次调用,对象会直接变成不可触及的状态,也就是说,一个对象的finalize方法只会被调用一次。
我还能再抢救一下
public class FinalizeEscapeGC {
public static FinalizeEscapeGC SAVE_HOOK = null;
public void isAlive(){
System.out.println("i am still alive");
}
@Override
protected void finalize() throws Throwable{
super.finalize();
System.out.println("finalize method executed!");
FinalizeEscapeGC.SAVE_HOOK = this;//抢救一次
}
public static void main(String[] args) throws InterruptedException {
SAVE_HOOK = new FinalizeEscapeGC();
SAVE_HOOK = null;
//对象第一次成功拯救自己
System.gc();
//Finalizer线程优先级较低,暂停一下
Thread.sleep(2000);
if(SAVE_HOOK != null){
SAVE_HOOK.isAlive();
}else {
System.out.println("① i am dead!");
}
//抢救失败
SAVE_HOOK = null;
System.gc();
Thread.sleep(2000);
if (SAVE_HOOK != null){
SAVE_HOOK.isAlive();
}else{
System.out.println("② i am dead!");
}
}
/**
* finalize method executed!
* i am still alive
* ② i am dead!
*/
}
版权属于:带翅膀的猫
本文链接:https://www.chengpengper.cn/archives/114/
转载时须注明出处及本声明