表示在底层实现的时候使用一个用于计数的变量进行计数,在每一次对一个变量或者实例进行引用之后,计数加一,当引用不使用之后,计数减一,直到计数为零的时候,这就代表着当前的这个变量或者实例没有被引用,也就是说可以被当成垃圾进行回收。
在这里,也会存在一个弊端,就是循环引用,在A和B对象之间一直互相引用,计数一直增加,就无法确定在什么时候才能进行回收。
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是Memory Analyzer的简称,它是一款功能强大的Java堆内存分析器。可以用于查找内存泄露以及查看内存消耗情况。MAT是基于Eclipse开发的,是一款免费的性能分析工具。可以在http://www.eclipse.org/mat/下载并使用MAT。
下载完成之后进行解压缩,双击exe文件打开。之后打开这两个bin文件,如下图所示就可以查看gc root
打开GC root 查看,分了四个大类:如下图所示:
在这里,我们获取数据,但是我们的内存有限,而这里的资源又不是那么重要,(添加一个参数 -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设置的小一点,这样的话垃圾回收机制就会被触发多次)在这里没有设置,在第七次执行后,第八次执行时,内存不够就会调用垃圾回收机制,将前面没有用的变量所占的内存进行回收,