欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JVM垃圾回收 -如何判断对象可以被回收(八)

程序员文章站 2024-03-17 21:04:22
...

一、引用计数算法

当对象引用了则这个对象加计数加1.再次引用了则计数变为2,如果不再引用了则计数减1,知道这个计数变为0,则对对象回收。
问题:
当两个对象相互引用时,虽然他们不会再被引用了,但他们的计数不能归为0,所以无法垃圾回收(如下图)。Java 没有使用此类算法

JVM垃圾回收 -如何判断对象可以被回收(八)

二、可达性分析算法

此算法的核心是:通过一系列的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 命令生成分析文件JVM垃圾回收 -如何判断对象可以被回收(八)

(2)、打开通过MAT工具打开生成的分析文件
JVM垃圾回收 -如何判断对象可以被回收(八)
(3)、在MAT工具上找到Open Query Browser ---->Java basice ---->GC roots
JVM垃圾回收 -如何判断对象可以被回收(八)
从上图可以看到有四类可以作为根对象:

  1. system class系统的加载类
    JVM垃圾回收 -如何判断对象可以被回收(八)

  2. Thread 活动的线程中局部变量所引用的对象可以作为root 对象
    JVM垃圾回收 -如何判断对象可以被回收(八)

  3. JNI Global
    JVM垃圾回收 -如何判断对象可以被回收(八)

  4. Busy Monitor 加锁的类
    JVM垃圾回收 -如何判断对象可以被回收(八)

三、五种引用类型

  1. 强引用
    我们平时使用的对象,都是强引用对象,只有所有的GC Roots对象都不通过强引用所引用的对象,才能被垃圾回收
  2. 弱引用
    当对象只有若引用引用该对象时,在执行垃圾回收时,无论内存是否充足,该对象都会被回收,
    可以通过配合引用队列,来释放弱引用自身,具体例子如下
      /**
	   * 演示弱引用, 配合引用队列
	   */
	    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());
	        }

	    }
  1. 软引用
    当该队像没有被强引用时,且只存在软引用,当执行垃圾回收时,发现内存不够用时,会回收软引用,它也可以配合配合引用对列使用

/**

  • 演示软引用, 配合引用队列
    */
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());
        }

    }
}
  1. 虚引用
    必须配合引用队列使用,主要是配和,Bytebuffer 使用吗,当被引用对象回收时,会将虚引用放入引用对列,由Refrence Handler 线程调用虚引用对象Cleaner 的方法去调用Unsafe.freeMemory()释放内存
  2. 终结器引用
    也必须配合引用对列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有回收),再由单独Finalizer线程通过终结器器引用找到被引用对象,并调用Finalize方法,第二次引用对象才能被回收
相关标签: JVM