【JVM垃圾回收】如何判断对象可以回收?

 2022-09-12
原文地址:https://blog.csdn.net/weixin_45379261/article/details/121797493

1、引用计数算法

  • 定义:判断一个对象是否受到引用, 如果受到一个引用就+1,如果减少一个引用就-1,当计数为0那么久标记为垃圾。

  • 弊端:循环引用时,两个对象的计数都为1,导致两个对象计数不能为0,不能被定义为垃圾。

    202209122055557101.png

2、可达性分析算法

  • JVM中的垃圾回收器通过可达性分析来探索所有存活的对象.
  • 扫描堆中的对象,看能否 沿着GC Root对象为起点的引用链找到该对象 ,如果找不到,则表示可以回收.
  • 可以作为GC Root的对象。【JVM垃圾回收】哪些可以当作GC ROOT?

3、五种引用

202209122055579502.png

1、强引用(A1):

只有GC Root都不引用该对象时,才会回收强引用对象

  • 如上图B、C对象都不引用A1对象时,A1对象才会被回收

2、软引用

当GC Root指向软引用对象时,在内存不足时,会回收软引用所引用的对象

  • 如上图如果B对象不再引用A2对象且内存不足时,软引用所引用的A2对象就会被回收.

软引用的使用 :

    public class Demo1 {
    	public static void main(String[] args) {
    		final int _4M = 4*1024*1024;
    		//使用软引用对象 list和SoftReference是强引用,而SoftReference和byte数组则是软引用
    		List<SoftReference<byte[]>> list = new ArrayList<>();
    		SoftReference<byte[]> ref= new SoftReference<>(new byte[_4M]);
    	}
    }

如果在垃圾回收时发现内存不足,在回收软引用所指向的对象时, 软引用本身不会被清理 ,如果想要清理软引用, 需要使用引用队列 ,ReferenceQueue<byte[]> queue = new ReferenceQueue<>();

    public class Demo1 {
    	public static void main(String[] args) {
    		final int _4M = 4*1024*1024;
    		//使用引用队列,用于移除引用为空的软引用对象
    		ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
    		//使用软引用对象 list和SoftReference是强引用,而SoftReference和byte数组则是软引用
    		List<SoftReference<byte[]>> list = new ArrayList<>();
    		SoftReference<byte[]> ref= new SoftReference<>(new byte[_4M]);
    
    		//遍历引用队列,如果有元素,则移除
    		Reference<? extends byte[]> poll = queue.poll();
    		while(poll != null) {
    			//引用队列不为空,则从集合中移除该元素
    			list.remove(poll);
    			//移动到引用队列中的下一个元素
    			poll = queue.poll();
    		}
    	}
    }

大概思路 为:如果内存不足,则查看引用队列中有无软引用,如果有,则将该软引用从存放它的集合中移除(这里为一个list集合)

202209122055597173.png

3、弱引用

只有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用所引用的对象.

  • 如上图如果B对象不再引用A3对象,则A3对象会被回收

弱引用的使用和软引用类似 ,只是将 SoftReference 换为了 WeakReference ,soft温柔的,weak软弱的。

4、虚引用

当虚引用对象所引用的对象被回收以后,虚引用对象就会被放入引用队列中,调用虚引用的方法

  1. 虚引用的一个体现是释放直接内存所分配的内存,当引用的对象ByteBuffer被垃圾回收以后,虚引用对象Cleaner就会被放入引用队列中,然后调用Cleaner的clean方法来释放直接内存.
  2. 如上图,B对象不再引用ByteBuffer对象,ByteBuffer就会被回收。但是直接内存中的内存还未被回收。这时需要将虚引用对象Cleaner放入引用队列中,然后调用它的clean方法来释放直接内存.

5、终结器引用

所有的类都继承自Object类,Object类有一个finalize方法。当某个对象不再被其他的对象所引用时,会先将终结器引用对象放入引用队列中,然后根据终结器引用对象找到它所引用的对象,然后调用该对象的finalize方法。调用以后,该对象就可以被垃圾回收了。

  • 如上图,B对象不再引用A4对象。这是终结器对象就会被放入引用队列中,引用队列会根据它,找到它所引用的对象。然后调用被引用对象的finalize方法。调用以后,该对象就可以被垃圾回收了.

引用队列

  • 软引用和弱引用可以配合引用队列

    • 在弱引用和虚引用所引用的对象被回收以后,会将这些引用放入引用队列中,方便一起回收这些软/弱引用对象
  • 虚引用和终结器引用必须配合引用队列

    • 虚引用和终结器引用在使用时会关联一个引用队列