JVM 可达性算法以及java引用类型
一、可达性算法
1、什么是对象可达
对象可达指的就是:双方存在间接或者直接的引用关系。根可达或者GC Roots可达就是指:对象到GC Roots存在间接或者直接的引用关系。
如下代码
public class MyObject {
private String objectName;//对象名
private MyObject refrence;//依赖对象
public MyObject(String objectName) {
this.objectName = objectName;
}
public MyObject(String objectName, MyObject refrence) {
this.objectName = objectName;
this.refrence = refrence;
}
public static void main(String[] args) {
MyObject a = new MyObject("a");
MyObject b = new MyObject("b");
MyObject c = new MyObject("c");
a.refrence = b;
b.refrence = c;
new MyObject("d", new MyObject("e"));
}
}
创建了5个对象,他们之间的引用关系如下图所示:
d、e虽然彼此之间存在引用,但是GC Roots不可达,因此可以被回收。
假设a是GC Roots的话,那么b、c就是可达的,d、e是不可达的
2、GC Roots是什么
垃圾回收时,JVM首先要找到所有的GC Roots ,这个过程叫做枚举根节点。这个过程是需要暂定用户线程的,即触发STW。然后再从GC Roots 这些根节点向下搜寻,不可达对象就回收。
那么GC Roots到底是指什么呢?
GC Roots 就是对象,而且是JVM确定当前绝对不能被回收的对象(如方法区中类静态属性引用的对象 )。
只有找到这种对象,后面的搜寻过程才有意义,不能被回收的对象所依赖的其他对象肯定也不能回收嘛。当JVM触发GC时,首先会让所有的用户线程到达安全点SafePoint时阻塞,也就是STW,然后枚举根节点,即找到所有的GC
Roots,然后就可以从这些GC Roots向下搜寻,可达的对象就保留,不可达的对象就回收。即使是号称几乎不停顿的CMS、G1等收集器,在枚举根节点时,也是要暂停用户线程的。
GC Roots是一种特殊的对象,是Java程序在运行过程中所必须的对象,而且是根对象。
3、那么哪些对象可以作为GC Roots对象
-
方法区静态属性引用的对象
全局对象的一种,Class对象本身很难被回收,回收的条件非常苛刻,只要Class对象不被回收,静态成员就不能被回收。 -
方法区常量池引用的对象
也属于全局对象,例如字符串常量池,常量本身初始化后不会再改变,因此作为GC Roots也是合理的。 -
方法栈中栈帧本地变量表引用的对象
属于执行上下文中的对象,线程在执行方法时,会将方法打包成一个栈帧入栈执行,方法里用到的局部变量会存放到栈帧的本地变量表中。只要方法还在运行,还没出栈,就意味这本地变量表的对象还会被访问,GC就不应该回收,所以这一类对象也可作为GC Roots。 -
JNI本地方法栈中引用的对象
和上一条本质相同,无非是一个是Java方法栈中的变量引用,一个是native方法(C、C++)方法栈中的变量引用。 -
被同步锁持有的对象
被synchronized锁住的对象也是绝对不能回收的,当前有线程持有对象锁呢,GC如果回收了对象,锁不就失效了嘛。
二、java中对象不同的引用类型
1、强引用
强引用就是类似下面代码
public class Kafka {
public static ReplicaManager replicaManager = new ReplicaManager();
}
是指创建一个对象并把这个对象赋给一个引用变量。
强引用有引用变量指向时永远不会被垃圾回收,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。
2、软引用
public class Kafka {
public static void main(String[] args){
MyObject aRef = new MyObject();
SoftReference aSoftRef=new SoftReference(aRef);
aRef = null;
}
}
上述代码就是弱引用
此时,对于这个MyObject对象,有两个引用路径,一个是来自SoftReference对象的软引用,一个来自变量aReference的强引用,所以这个MyObject对象是强可及对象。
随即,我们可以结束aReference对这个MyObject实例的强引用:
aRef = null*;*
此后,这个MyObject对象成为了软引用对象。如果垃圾收集线程进行内存垃圾收集,并不会因为有一个SoftReference对该对象的引用而始终保留该对象。
Java虚拟机的垃圾收集线程对软可及对象和其他一般Java对象进行了区别对待:软可及对象的清理是由垃圾收集线程根据其特定算法按照内存需求决定的。垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收软可及对象
3、弱引用
弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。下面是使用示例:
public class test {
public static void main(String[] args) {
WeakReference<People>reference=new WeakReference<People>(new People("zhouqian",20));
System.out.println(reference.get());
System.gc();//通知JVM回收资源
System.out.println(reference.get());
}
}
class People{
public String name;
public int age;
public People(String name,int age) {
this.name=name;
this.age=age;
}
@Override
public String toString() {
return "[name:"+name+",age:"+age+"]";
}
}
第二个输出结果是null,这说明只要JVM进行垃圾回收,被弱引用关联的对象必定会被回收掉。不过要注意的是,这里所说的被弱引用关联的对象是指只有弱引用与之关联,如果存在强引用同时与之关联,则进行垃圾回收时也不会回收该对象(软引用也是如此)