JVM中如何判断对象是否存活?
引用计数法
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时,计数器值减1;任何时刻计数器值为零的对象就是不可能再被使用的对象。引用计数法虽然占用了一些额外的内存空间来进行计数,当它的原理简单,判断效率也很高,在大多数情况下是一个不错的算法。
这个方式判断起来很简单而且效率很高因此有很多应用案例,但在java中并没有使用这种方式,因为它无法解决循环引用的问题,例如对象A和对象B都有字段instance,A.instance = B , B.instance = A;这两个对象不可能再被外部访问但是由于他们互相之间引用着对象,导致他们的引用计数器值不为0,因此也无法将其回收。(Java虚拟机未使用该方式)
可达性分析法
这个算法的基本思路就是通过一系列称为”GC Roots“的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为”引用链“,如果某个对象到GC Roots间没有任何引用链相连即GC Roots到这个对象不可达时,则证明该对象不可能再被使用因此会被回收。
GC Roots的对象:
虚拟机栈中引用的对象
方法区中常量引用的对象,例如字符串常量池里的引用
在本地方法栈中JNI引用的对象。
Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象等,还有系统类加载器。
所有被同步锁持有的对象。
反应java虚拟机内部情况的JMXBean,JVMTI中注册的回调,本地代码缓存等。
public class JvmDemo {
//方法区中的类静态属性引用的对象。
private static SemaphoreDemo semaphoreDemo;
private final Object lock = new Object();
public void synchronizedMethod() {
synchronized (lock) {
// 被同步锁持有的对象
System.out.println("Lock is held by: " + Thread.currentThread().getName());
}
}
//方法区中常量引用对象。
private static final Integer A = 1;
public static void m1(){
//虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。
JvmDemo demo = new JvmDemo();
}
public static void main(String[] args) {
//虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。
m1();
String str2 = "Hello"; // 字符串常量池中引用的对象
//本地方法中 JNI(Native 方法)引用的对象。
new Thread().start();
}
}
除了这些固定的GC Roots集合以外,根据用户所选用的垃圾收集器以及当前回收的内存区域不同,还可以有其他对象临时性加入,共同构成完整GC Roots集合。如果只针对Java堆中的某一块区域发起垃圾收集时,必须考虑到内存区域是虚拟机自己的实现细节,更不是孤立封闭的,所以某个区域里的对象完全有可能被位于堆中其他区域的对象所引用,这时候就需要将这些关联区域的对象也一并加入GC Roots集合中,才能保证可达性分析的正确性。
什么是引用?
无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否引用链可达,判定对象是否存活都和”引用“离不开关系。jdk1.2之前,java中的引用都是很传统的定义:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称该reference数据是代表某块内存,某个对象的引用。
Java引用按照强度依次有四种:
强引用:最传统的引用的定义,在程序中普遍存在的引用赋值例如 Object obj = new Object() 。无论在任何情况下,只要强引用关系还存在,垃圾收集器就永远不可能回收掉该对象。
Object strongReference = new Object();
软引用:该引用是用来描述一些还有用,但非必须的对象,只被软引用关联的对象在系统将要发生内存溢出异常前,会将这些对象列进回收范围进行第二次回收,若回收后内存还是不够则抛出内存溢出异常。
SoftReference<Object> softReference = new SoftReference<>(strongObject);
弱引用(WeakReference):它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾回收发生为止。因为当垃圾收集开始工作时无论内存是否充足都会将弱引用关联的对象回收。
WeakReference<Object> weakReference = new WeakReference<>(strongObject);
虚引用:虚引用也称幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例(当然也无法访问对象上的方法)。为一个对象设置虚引用关联的目的就是为了能在这个对象被收集器回收时收到一个系统通知。
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>(); // 创建引用队列 当虚引用被回收后会被放置到该队列中
Object strongObject = new Object();// 创建一个对象并使用强引用引用它
PhantomReference<Object> phantomReference = new PhantomReference<>(strongObject, referenceQueue);// 创建一个虚引用,并将它与引用队列关联