JVM垃圾回收篇(对象终止机制)
Posted ProChick
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM垃圾回收篇(对象终止机制)相关的知识,希望对你有一定的参考价值。
1.什么是对象终止机制?
- Java语言提供了对象终止(
finalization
)机制来允许开发人员提供对象被销毁之前的自定义处理逻辑处理 - 当垃圾回收器发现没有任何引用指向某个对象时,那么就会在垃圾回收中清除这个对象,在垃圾回收器回收此对象之前,会先调用这个对象的
finalize()
方法 - 我们发现
finalize()
方法允许在子类中被重写,所以我们可以利用这个重写的方法,用于在对象被回收时进行资源的释放操作或者一些清理操作,比如:关闭文件、套接字和数据库连接等 - 从功能上来说,
finalize()
方法与C++ 中的析构函数
比较相似,但是Java采用的是基于垃圾回收器的自动内存管理机制,它们在本质上还有区别
2.对象终止的注意细节
finalize()
方法在一个对象生命周期中,只会被调用一次(即使被复活过
)- 我们开发人员切记不要主动调用某个对象的
finalize()
方法,而是应该交给垃圾回收机制去调用 - 因为在
finalize()
方法中,我们可以导致对象重新复活,也就是不让垃圾回收器回收,这会影响垃圾回收器本身的工作 - 因为
finalize()
方法的执行时间是没有保障的,它完全由GC线程决定,也就是说,若不发生GC,那么finalize()
方法将永远没有机会执行 - 因为一个糟糕的
finalize()
方法会严重影响GC的性能,所以也不建议自己去调用
3.对象的三种生命状态
由于finalize()方法的存在,虚拟机中的对象一般会有三种可能的状态
如果从所有的根节点都无法访问到某个对象,说明对象己经不再使用了。一般来说,此对象需要被回收。但事实上,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段。因为一个无法触及的对象有可能在某一个条件下“复活”自己,如果这样,那么对它的回收就是不合理的
可触及的状态
:从GC Roots根节点开始,可以到达这个对象可复活的状态
:对象的所有引用都已经被释放,但是对象有可能在finalize()
方法中复活不可触及的状态
:对象的finalize()
被调用后发现也没有被复活,那么就会进入不可触及状态,不可触及的对象不可能再被复活,因为finalize()
只会被调用一次
4.对象的回收判定过程
判定一个对象A是否可回收,至少要经历两次标记过程
- 如果对象A到GC Roots没有引用链,则进行第一次标记
- 如果对象A没有重写
finalize()
方法,或者finalize()
方法已经被虚拟机调用过了,则虚拟机视为“没有必要执行”,此时对象A被判定为不可触及的状态 - 如果对象A重写了
finalize()
方法,且还未执行过,那么对象A会被插入到F-Queue
的队列中,然后由虚拟机自动创建的、低优先级的Finalizer
线程触发其finalize()
方法执行 - 对象A会被插入到
F-Queue
的队列中后, GC会对这个队列中的所有对象进行第二次标记 - 如果对象A在
finalize()
方法中也没有与引用链上的任何一个对象建立联系,那么在第二次标记时,对象A就会被判定为不可触及的状态 - 如果对象A在
finalize()
方法中与引用链上的任何一个对象建立了联系,那么在第二次标记时,对象A就会被判定为可复活的状态。 但是当对象再次出现没有引用存在的情况时,finalize()
方法也不会被再次调用,直接被判定为不可触及的状态
5.对象的回收程序演示
public class CanReliveObj {
public static CanReliveObj obj;
// 重写finalize方法(第二次标记)
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("调用当前类重写的finalize()方法");
obj = this;
}
public static void main(String[] args) {
try {
obj = new CanReliveObj();
obj = null;
System.gc(); // 第一次标记
System.out.println("第1次 gc");
Thread.sleep(2000);
if (obj == null) {
System.out.println("obj is dead");
} else {
System.out.println("obj is still alive");
}
obj = null;
System.gc();
System.out.println("第2次 gc");
Thread.sleep(2000);
if (obj == null) {
System.out.println("obj is dead");
} else {
System.out.println("obj is still alive");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
当把上面重写的finalize方法取消时
当把上面重写的finalize方法加上时
6.什么是GC Roots?
上面提到的GC Roots是什么,它是垃圾回收器在垃圾回收时进行判断的根搜索对象集合,那么如何利用工具查看代码中的GC Roots呢?
编写测试代码
public class GCRootsTest {
public static void main(String[] args) {
List<Object> numList = new ArrayList<>();
Date birth = new Date();
for (int i = 0; i < 100; i++) {
numList.add(String.valueOf(i));
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("数据添加完毕...");
new Scanner(System.in).next();
numList = null;
birth = null;
System.out.println("对象已被置空...");
}
}
获取堆内存的Dump文件
-
命令行方式
# 查看当前进程 jps # 生成dump文件 jmap -dump:format=b,live,file=test1.bin 进程ID
-
工具导出方式
使用 JVisualVM 工具
使用工具分析dump文件
-
MAT工具
MAT是 Memory Analyzer 的简称, 是由Eclipse开发的,它是一 款功能强大的Java堆内存分析器,用于查找内存泄漏以及查看内存消耗情况,下载地址: http://www.eclipse/org/mat/
-
JProfiler工具
以上是关于JVM垃圾回收篇(对象终止机制)的主要内容,如果未能解决你的问题,请参考以下文章