今天记录并巩固下学到的JVM垃圾回收的相关知识,在JVM中,垃圾回收主要发生在堆中,在回收对象前JVM首先需要确认哪些对象是可回收的,一般判断对象是否应该回收有两种方式:
引用计数算法
顾名思义,引用计数算法其实就是计算某个对象的引用数。首先为每个对象添加一个引用计数器,当有一个地方引用了某一对象,则该计数器加1;当某一地方不在引用时,则引用计数器减1。引用计数算法很实用,且使用起来效率也很不错,但是无法解决对象间的循环引用从而导致每个对象的引用计数器都不为0-----GC无法对对象进行回收。
可达性分析算法
目前JVM的主要使用的算法。该算法的核心就是通过可达性来作为判断对象是否可被回收的依据。通过一系列称为“GC-ROOT”的对象作为起点向下进行检索,检索的路径称为引用链,若某一对象对于所有引用链均不可达,则可判断该对象为可回收。在JAVA中,可作为GC-ROOT节点的为:
1 虚拟机栈中引用的对象;
2 方法区中类中静态属性引用的对象;
3 方法区中常量引用的对象;
4 本地方法栈中引用的对象。
JAVA中对引用进行了划分,使我们能够更好地管理对象以及内存,划分出的引用类型为强,软,弱,虚4中类型:
1 强引用(Strong Reference):对于拥有强引用的对象,JVM在任何时候都不会回收
2 软引用(Soft Reference):对于拥有软引用的对象,只有当JVM内存不足时才会被回收
3 弱引用(Weak Reference):对于拥有弱引用的对象,无论内存是否够,JVM都会在下一个GC回收周期将该对象回收
4 虚引用(Phantom Reference):对于拥有虚引用的对象,当该对象被回收时收到一个系统通知
当某一对象被判定“不可达”时,JVM不会立刻回收,而是会进行至少两次标记,在第一次标记之后进行筛选,筛选条件为是否可执行finalize()方法,若该对象没有覆盖finalize()方法或者finalize()方法已经被调用过,则JVM不会执行该方法。
在调用finalize()方法之后若该对象与任意GC-ROOT建立了联系,则不会被JVM回收,反之则被回收。注意:同一个对象finalize()方法只会被执行一次。
事实上虽然不是必须,但是方法区也是可以被垃圾回收的,回收的主要为两类:废弃常量与无用的类。常量的回收机制与对象相似,若某一在常量池的常量未被引用,则在必要到的情况下可被回收。而判断类是否无用则需要以下3个条件:
1 在JAVA中不存在该类的实例;
2 该类对应的ClassLoader已经被回收;
3 该类对应的 java.lang.Class 对象不存在引用,无法在任何地方通过反射机制访问该类的方法。
当我们需要大量使用反射或者频繁自定义ClassLoader时,可以考虑对方法区进行垃圾回收。