JVM 垃圾回收(一)如何判断对象可以回收

it2023-01-09  54

垃圾回收的方式

1. 引用计数法

表示在底层实现的时候使用一个用于计数的变量进行计数,在每一次对一个变量或者实例进行引用之后,计数加一,当引用不使用之后,计数减一,直到计数为零的时候,这就代表着当前的这个变量或者实例没有被引用,也就是说可以被当成垃圾进行回收。

在这里,也会存在一个弊端,就是循环引用,在A和B对象之间一直互相引用,计数一直增加,就无法确定在什么时候才能进行回收。

2. 可达性分析算法

Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象 扫描堆中的对象,看是否能够沿着 GC Root 对象为起点的引用链找到该对象,找不到,表示可以回收

那怎么查看这个GC root 有哪些呢,我们使用一段简单的代码进行测试,在这里检测两个状态,运行代码。

import java.io.IOException; import java.util.ArrayList; import java.util.List; public class 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..."); } }

使用jmap工具抓取进程快照,使用命令 jmap -dump:format=b,live,file=1.bin 8208

b 表示二进制文件live 当前还存活的对象,也就是会触发一次垃圾回收file 表示将快照抓取出来,另存为一个什么文件最后面表示当前进程id

使用命令进行检测

得到了两个文件,但是怎么进行分析呢,在这里我们使用eclipes提供的MAT工具

MAT内存分析工具

MAT是Memory Analyzer的简称,它是一款功能强大的Java堆内存分析器。可以用于查找内存泄露以及查看内存消耗情况。MAT是基于Eclipse开发的,是一款免费的性能分析工具。可以在http://www.eclipse.org/mat/下载并使用MAT。

下载完成之后进行解压缩,双击exe文件打开。之后打开这两个bin文件,如下图所示就可以查看gc root

打开GC root 查看,分了四个大类:如下图所示:

3. 四种引用

强引用 只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收软引用(SoftReference) 仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象 可以配合引用队列来释放软引用自身弱引用(WeakReference) 仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象 可以配合引用队列来释放弱引用自身虚引用(PhantomReference) 必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法释放直接内存终结器引用(FinalReference) 无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象 暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize方法,第二次 GC 时才能回收被引用对象

4. 软引用案例

在这里,我们获取数据,但是我们的内存有限,而这里的资源又不是那么重要,(添加一个参数 -Xmx20m 进行模拟)我们使用强引用的话,会出现堆内存溢出的报错。使用以下代码测试

private static final int _4MB = 4 * 1024 * 1024; public static void main(String[] args) throws IOException { List<byte[]> list = new ArrayList<>(); for (int i = 0; i < 5; i++) { list.add(new byte[_4MB]); }

使用软引用进行实现:添加一个软引用,在这里的连接引用也是由一个强引用之间加上了一个软引用。

public static void soft() { // list --> SoftReference --> byte[] List<SoftReference<byte[]>> list = new ArrayList<>(); for (int i = 0; i < 5; i++) { SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]); System.out.println(ref.get()); list.add(ref); System.out.println(list.size()); } System.out.println("循环结束:" + list.size()); for (SoftReference<byte[]> ref : list) { System.out.println(ref.get()); } }

运行代码,添加了软引用之后,内存不足的时候会触发垃圾回收机制,所以在这里代码是不会导致堆内存溢出的。垃圾回收之后,list当中的值会被清除为null。

软引用与引用队列

在这里软引用虽然为null,但是也会占据内存空间,在这里我们使用一个引用队列对这个软引用进行清除。代码实现如下:

import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.List; public class queue { 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()); } } }

在最后获取这个list就只会存在一个数值,其余的null值均被清空。

弱引用

弱引用对象我们和前面的测试一致,一直添加,并且我们给虚拟机加上参数 -XX:+PrintGCDetails -verbose:gc 表示打印垃圾回收的相关信息。

import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; public class WeakReference_test { private static final int _4MB = 4 * 1024 * 1024; public static void main(String[] args) { // list --> WeakReference --> byte[] List<WeakReference<byte[]>> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { WeakReference<byte[]> ref = new WeakReference<>(new byte[_4MB]); list.add(ref); for (WeakReference<byte[]> w : list) { System.out.print(w.get()+" "); } System.out.println(); } System.out.println("循环结束:" + list.size()); } }

运行代码,查看结果(在这里,我们也可以将-Xmx设置的小一点,这样的话垃圾回收机制就会被触发多次)在这里没有设置,在第七次执行后,第八次执行时,内存不够就会调用垃圾回收机制,将前面没有用的变量所占的内存进行回收,

最新回复(0)