JVM如何判断对象已死?
一、JVM如何判断对象已死?
堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的对象)。
1.1 引用计数法
给对象添加一个引用计数器,每当一个地方引用它object时技术加1,引用失去以后就减1,计数为0说明不再引用。
优点:实现简单,判定效率高;
缺点:无法解决对象相互循环引用的问题,对象A中引用了对象B,对象B中引用对象A。对应代码如下:
public class A {
public B b;
}
public class B {
public C c;
}
public class C {
public A a;
}
public class Test{
private void test(){
A a = new A();
B b = new B();
C c = new C();
a.b=b;
b.c=c;
c.a=a;
}
}
1.2 可达性分析
这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots
没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。
那么,所谓的**“GC Roots”**指的是什么呢?
- 虚拟机栈(栈帧中的局部变量表)中引用的对象
- 本地方法栈(Native 方法)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 所有被同步锁持有的对象
- JNI(Java Native Interface)引用的对象
详情
复习Java的运行时内存
线程私有:虚拟机栈、本地方法栈、程序计数器
线程共有: 堆(还包含一个字符串常量池)、方法区
二、Java的四种引用类型
无论是通过引用计数法判断对象引用数量,还是通过可达性分析判断对象的引用链是否可达,判定对象的存活都与“引用”有关。
那么,这个所谓的“引用”又是什么东西?
现在Java将引用分为强引用、软引用、弱引用、虚引用四种(引用强度逐渐减弱),强引用就是 Java 中普通的对象,而软引用、弱引用、虚引用在 JDK 中定义的类分别是 SoftReference
、WeakReference
、PhantomReference
。
2.1 强引用
强引用实际上就是程序代码中普遍存在的引用赋值,这是使用最普遍的引用,例如以下代码:
//强引用示例
String strongReference = new String("abc");
String strongReference2 = "abc";
详情
两种创建字符串的方式:
String strongReference = "abc";
- 这种方式是通过字符串常量池来创建字符串对象的。
- 当你使用双引号定义字符串时,Java会先检查字符串常量池中是否已经存在一个值
String strongReference = new String("abc");
- 这种方式是通过
new
关键字创建字符串对象的。 - 每次调用
new String("abc")
都会在堆内存中创建一个新的字符串对象,即使字符串的内容相同。
这两种方式都是产生的强引用
如果一个对象具有强引用,那么垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError
错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题
2.2 软引用
软引用允许垃圾收集器在内存不足时回收被软引用指向的对象,可以通过以下方式来创建:
// 软引用
String str = new String("abc");
SoftReference<String> softReference = new SoftReference<String>(str);
如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。
软引用可用来实现内存敏感的高速缓存,当内存不足时,可以自动释放缓存对象。
2.3 弱引用
只具有弱引用的对象拥有更短暂的生命周期。
在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
2.4 虚引用
虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。
虚引用主要用来跟踪对象被垃圾回收的活动。但是实际开发中用的也比较少,用的最多的还是软引用。