jvm 判断对象是否已经死亡
如何判断java对象已经死亡?
容易想到的就是引用计数算法,就说的是给对象添加一个引用计数器,每当有一个地方引用到他,就加1;引用失效就减1。但是这样做是有问题的。
看下例子:
public class ReferenceCountingGC {
public Object instace = null;
// 一个200M的对象
private byte[] bigSize = new byte[200*1024*1024];
public static void main(String[] args) {
// 引用加1
ReferenceCountingGC a = new ReferenceCountingGC();
ReferenceCountingGC b = new ReferenceCountingGC();
// 引用再加1
a.instace=b;
b.instace=a;
// 如果是引用计数,那么a,b的引用为2
// 在这里a,b减1
a = null;
b = null;
// 那么在这里ab引用都不是0,应该不可以回收
System.gc();
sleep(30);
}
public static void sleep(int second){
try {
Thread.sleep(second*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
debug,通过VisualVM发现,内存仍然被回收了。
,所以JVM不是通过引用计数的方式来确定是否回收对象的。因为,引用计数从上面的例子看出来会有一个问题就是,虽然还有引用,但这两个对象都是不可达对象(无法被访问的)。
所以java的垃圾回收不是采用这种简陋的引用计数来实现的。
可达性分析算法
java是通过可达性分析(Reachability Analysis)算法来判断对象是否存活。
其基本思想是通过一系列“”GC Roots“”的对象为起始点,从这些节点向下搜索,搜索所走过的路称为引用链,当一个对象没有任何引用链相连时,则证明该对象是不可引用的。如图(o1,o2,o3为不可达对象):
GC Root对象
1.虚拟机栈中引用的对象。
2.方法区中类静态属性引用的对象。
3.方法区常量引用的对象。
4.本地方法栈中JNI引用的对象。
引用分类
强引用(StrongReference),软引用(SoftReference),弱引用(WeakReference)以及PhantomReference(虚引用)
1.如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
2.软引用他的特点是当内存足够时不会回收这种引用类型的对象,只有当内存不够用时才会回收。
3.弱引用的作用在于解决强引用所带来的对象之间在存活时间上的耦合关系。弱引用最常见的用处是在集合类中,尤其在哈希表中。哈希表的接口允许使用任何Java对象作为键来使用。当一个键值对被放入到哈希表中之后,哈希表对象本身就有了对这些键和值对象的引用。
4.虚引用的特点是只要GC一运行就会把他给回收了,但会得到一个系统通知。
不可达对象就一定死亡吗?
其实不可达对象也并非是‘’非死不可‘’,但是他们已经处于缓刑状态。
要真的宣布一个对象死亡要经过两次标记过程:第一次标记并且筛选,筛选的条件是是否有必要执行了finalize方法。如果该类重写了该方法,并且没有执行过,那么该方法将会被执行。如果该方法执行完成,对象又重新和GCRoot关联上(把自己的引用给GCRoot),那么他在下一次GC时就不会被回收。
具体来看一个例子。
public class FinalEscapeGC {
public static FinalEscapeGC obj = null;
public void isAlive(){
System.out.println("yes I'm still alive");
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize method executed!");
FinalEscapeGC.obj = this;
}
/**
* finalize method executed!
yes I'm still alive
no I'm dead!
*/
public static void main(String[] args) throws InterruptedException {
obj = new FinalEscapeGC();
// 第一次GC自救成功
obj = null;
System.gc();
// finalize方法优先级很低,因此要等他执行,不行还没执行就被回收了
Thread.sleep(500);
if(obj == null)
System.out.println("no I'm dead!");
else
obj.isAlive();
// 第二次自救失败
obj = null;
System.gc();
Thread.sleep(500);
if(obj == null)
System.out.println("no I'm dead!");
else
obj.isAlive();
}
}
这个例子说明两点:
1,finalize方法是对象最后一个活下去的机会;
2,机会只能使用一次。
警告:finalize方法知道就行,千万别用,可以使用finally代替
总结:
完整的从JVM垃圾回收的角度分析了对象是否死亡,对象是否该回收等知识点。要点:使用可达性分析算法发现对象是否可回收,GCRoot引用有哪些。引用的分级,finalize方法啥时候调用。
下一篇: ES6 将数组根据某个属性进行分组的方法