JVM——垃圾回收资格的判定
一:判断一个对象是否已死
1:引用数算法:给对象加个引用计数器,被引用时加一,引用失效减一,在任何时刻一直为0的就说明不会被使用,但是由于一种情况的存在,导致这种算法不被JVM所考虑,在两个对象相互引用用的时候,会出现计数一直为1的情况出现,如下代码所示:
public class Test { public Object instance = null; public static void main(String[] args) { Test a = new Test(); Test b = new Test(); a.instance = b; b.instance = a; } }
所以JVM不使用计数算法作为判断是否可回收的依据。
2:可达性分析算法:通过一系列的被称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索的路径称为引用链(Reference Chain),当一个对象到“GCRoots”没有任何引用链相连(从GCRoots到这个对象不可到达)时,则证明对象不可用,如图:
object 4,5,6虽然有互相关联,但是到GC Roots时不可达的,所以会被判定为时可回收对象。
在JAVA中,可作为GC Roots的对象包括以下几种:
- 虚拟机栈中局部变量表中引用的对象
- 本地方法栈中 JNI(一般说的Native方法) 中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中的常量引用的对象
3:引用类型:无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否可达,判定对象是否可被回收都与引用有关。
1:强引用:
被强引用关联的对象不会被回收。
使用 new 一个新对象的方式来创建强引用。
Object obj = new Object();
2:软引用:用来描述一些还有用但非必须的对象,在内存不够的情况下回收,回收之后还是内存不足才会抛出内存溢出异常,使用 SoftReference 类来创建软引用:
Object obj = new Object(); SoftReference<Object> sf = new SoftReference<Object>(obj); obj = null; // 使对象只被软引用关联
3:弱引用:也是用来描述非必需对象,但是只能生存到下一次垃圾回收,可使用WeakReference类来实现引用
Object obj = new Object(); PhantomReference<Object> pf = new PhantomReference<Object>(obj); obj = null
4:虚引用又称为幽灵引用或者幻影引用,一个对象是否有虚引用的存在,不会对其生存时间造成影响,也无法通过虚引用得到一个对象。为一个对象设置虚引用的唯一目的是能在这个对象被回收时收到一个系统通知。使用 PhantomReference 来创建虚引用。
Object obj = new Object(); PhantomReference<Object> pf = new PhantomReference<Object>(obj); obj = null;
4:最后的抉择:就算再可达性算法中不可达的对象,也不是非死不可的,要真正宣告死亡至少要经历两次标记过程:如果对象进行可达性分析后发现没有与GC Roots相连的引用链,就会被第一次标记并进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法,当对象没覆盖finalize()方法或者已经执行过finalize()方法,虚拟机将这两种情况都视作没有必要执行。如果判定为有必要执行的话,就会进入队列,等待触发finalize()方法,finalize()方法是对象最后一次拯救自己的机会,若是再第二次还没有逃脱,便死了。并且任何对象只能调用一次finalize()方法。
5:回收方法区:因为方法区主要存放永久代对象,而永久代对象的回收率比新生代低很多,所以在方法区上进行回收性价比不高。
主要是对常量池的回收和对类的卸载。
为了避免内存溢出,在大量使用反射和动态代理的场景都需要虚拟机具备类卸载功能。
类的卸载条件很多,需要满足以下三个条件,并且满足了条件也不一定会被卸载:
- 该类所有的实例都已经被回收,此时堆中不存在该类的任何实例。
- 加载该类的 ClassLoader 已经被回收。
- 该类对应的 Class 对象没有在任何地方被引用,也就无法在任何地方通过反射访问该类方法。
posted on 2019-07-21 11:26 CodeFish-Xiao 阅读(342) 评论(0) 编辑 收藏 举报