JVM垃圾回收 -如何判断对象可以被回收(八)
程序员文章站
2024-03-17 21:04:22
...
一、引用计数算法
当对象引用了则这个对象加计数加1.再次引用了则计数变为2,如果不再引用了则计数减1,知道这个计数变为0,则对对象回收。
问题:
当两个对象相互引用时,虽然他们不会再被引用了,但他们的计数不能归为0,所以无法垃圾回收(如下图)。Java 没有使用此类算法
二、可达性分析算法
此算法的核心是:通过一系列的GC ROOT 作为起点,沿着引用链向下寻找,如果找不到,则表示该对象可以会被回收。name什么对象可以 作为Root 起点呢?可以通过(jmap -dump:format (抓取文件的格式),live(触发回收存活的对象),file=test.bin(存储的文件) 11000(进程号)) 命令结合 Eclipse Memory Analyzer(MAT)工具去查找他的根节点。
下面是一段抓取GC Root 对象测试案例:
public static void main(String[] args) throws InterruptedException, IOException {
List<Object> list1 = new ArrayList<>();
list1.add("a");
list1.add("b");
System.out.println(1);
System.in.read();
list1 = null;
System.out.println(2);
System.in.read();
System.out.println("end...");
}
(1)、运行代码,使用jmap 命令生成分析文件
(2)、打开通过MAT工具打开生成的分析文件
(3)、在MAT工具上找到Open Query Browser ---->Java basice ---->GC roots
从上图可以看到有四类可以作为根对象:
-
system class系统的加载类
-
Thread 活动的线程中局部变量所引用的对象可以作为root 对象
-
JNI Global
-
Busy Monitor 加锁的类
三、五种引用类型
- 强引用
我们平时使用的对象,都是强引用对象,只有所有的GC Roots对象都不通过强引用所引用的对象,才能被垃圾回收 - 弱引用
当对象只有若引用引用该对象时,在执行垃圾回收时,无论内存是否充足,该对象都会被回收,
可以通过配合引用队列,来释放弱引用自身,具体例子如下
/**
* 演示弱引用, 配合引用队列
*/
private static final int _4MB = 4 * 1024 * 1024;
public static void main(String[] args) {
List<WeakReference<byte[]>> list = new ArrayList<>();
// 引用队列
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
for (int i = 0; i < 5; i++) {
// 关联了引用队列, 当软引用所关联的 byte[]被回收时,软引用自己会加入到 queue 中去
WeakReference<byte[]> ref = new WeakReference<>(new byte[_4MB], queue);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
}
// 从队列中获取无用的 软引用对象,并移除
Reference<? extends byte[]> poll = queue.poll();
while( poll != null) {
list.remove(poll);
poll = queue.poll();
}
System.out.println("===========================");
for (WeakReference<byte[]> reference : list) {
System.out.println(reference.get());
}
}
- 软引用
当该队像没有被强引用时,且只存在软引用,当执行垃圾回收时,发现内存不够用时,会回收软引用,它也可以配合配合引用对列使用
/**
- 演示软引用, 配合引用队列
*/
public class Demo {
private static final int _4MB = 4 * 1024 * 1024;
public static void main(String[] args) {
List<SoftReference<byte[]>> list = new ArrayList<>();
// 引用队列
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
for (int i = 0; i < 5; i++) {
// 关联了引用队列, 当软引用所关联的 byte[]被回收时,软引用自己会加入到 queue 中去
SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
}
// 从队列中获取无用的 软引用对象,并移除
Reference<? extends byte[]> poll = queue.poll();
while( poll != null) {
list.remove(poll);
poll = queue.poll();
}
System.out.println("===========================");
for (SoftReference<byte[]> reference : list) {
System.out.println(reference.get());
}
}
}
- 虚引用
必须配合引用队列使用,主要是配和,Bytebuffer 使用吗,当被引用对象回收时,会将虚引用放入引用对列,由Refrence Handler 线程调用虚引用对象Cleaner 的方法去调用Unsafe.freeMemory()释放内存 - 终结器引用
也必须配合引用对列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有回收),再由单独Finalizer线程通过终结器器引用找到被引用对象,并调用Finalize方法,第二次引用对象才能被回收
上一篇: JVM内存模型及垃圾收集策略解析(2) 博客分类: java
下一篇: 如何判断对象可以被回收?