栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【代码/图 Demo解读】JVM中的强引用、软引用、弱引用、虚引用、终结器引用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【代码/图 Demo解读】JVM中的强引用、软引用、弱引用、虚引用、终结器引用

下图实线表示强引用。

强引用

只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收。如果GC Roots一直通过强引用 引用该对象,那么垃圾回收器绝不会回收它,当内存空间不足,Java虚拟机宁愿抛出OOM异常,使程序异常终止。

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

例如网络读取图片资源时,如果是用强引用 引用这些存放图片资源的bytes数组,在内存不足时会OOM。这些不太重要的资源,在内存紧张时,应该要释放掉,之后再重新读取即可。所以不要使用强引用引用这些bytes数组了,而应该采取其它的引用。

所以,需要用软引用/弱引用。


最后一个byte数组保留下来。

关联引用队列,去掉无用的软引用:


具体讲解软引用、弱引用、虚引用如下:

软引用

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

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

设置一下VM参数:

-Xmx20M //设置堆内存空间大小20MB
-verbose:gc //在控制台输出GC情况
-XX:+PrintGCDetails  //在控制台输出详细的GC情况
软引用的使用
public class Demo1 {
	public static void main(String[] args) {
		final int _4M = 4*1024*1024;
		//使用软引用对象 list和SoftReference是强引用,而SoftReference和byte数组则是软引用
		List> list = new ArrayList<>();
		SoftReference ref= new SoftReference<>(new byte[_4M]);
	}
}

如果在垃圾回收时发现内存不足,在回收软引用所指向的对象时,软引用本身不会被清理

如果想要清理软引用,需要使用引用队列

public class HelloWorld {
    public static final int _4MB = 4*1024*1024;
    public static void main(String[] args) {

        List> list = new ArrayList<>();
        ReferenceQueue queue = new ReferenceQueue<>();
        for(int i = 0;i < 5;i ++){
            //这里将软引用对象和引用队列进行关联。注:现在并没有直接放到引用队列中。
            //当内存不足时,软引用指向的对象会被GC,软引用对象本身不会被清理,而是放到引用队列中。
            SoftReference softReference = new SoftReference<>(new byte[_4MB],queue);
            list.add(softReference);
        }
        //再查看引用队列中有无软引用
        Reference poll = queue.poll();
        while(poll!=null){
            //从存放软引用的集合中移除。
            list.remove(poll);
            poll = queue.poll();
        }
        for(SoftReference softReference: list){
            System.out.println(softReference.get());
        }
    }
}

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

弱引用

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

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

弱引用的使用和软引用类似,只是将 SoftReference 换为了 WeakReference

public class HelloWorld {
    public static final int _4MB = 4*1024*1024;
    public static void main(String[] args) {

        List> list = new ArrayList<>();
        ReferenceQueue queue = new ReferenceQueue<>();
        for(int i = 0;i < 5;i ++) {
            //这里将软引用对象和引用队列进行关联。注:现在并没有直接放到引用队列中。
            //当内存不足时,软引用指向的对象会被GC,软引用对象本身不会被清理,而是放到引用队列中。
            WeakReference WeakReference = new WeakReference<>(new byte[_4MB], queue);
            list.add(WeakReference);
            for (WeakReference weakReference : list) {
                System.out.print(weakReference.get() +" ");
            }
            System.out.println();
        }
    }
}

输出:

[B@1b6d3586 
[B@1b6d3586 [B@4554617c 
[B@1b6d3586 [B@4554617c [B@74a14482 
[GC (Allocation Failure) [PSYoungGen: 1784K->488K(6144K)] 14073K->13000K(19968K), 0.0013709 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[B@1b6d3586 [B@4554617c [B@74a14482 [B@1540e19d 
[GC (Allocation Failure) [PSYoungGen: 4696K->496K(6144K)] 17208K->13104K(19968K), 0.0011455 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[B@1b6d3586 [B@4554617c [B@74a14482 null [B@677327b6 
Heap
 PSYoungGen      total 6144K, used 4761K [0x00000000ff980000, 0x0000000100000000, 0x0000000100000000)
  eden space 5632K, 75% used [0x00000000ff980000,0x00000000ffdaa540,0x00000000fff00000)
  from space 512K, 96% used [0x00000000fff80000,0x00000000ffffc020,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 13824K, used 12608K [0x00000000fec00000, 0x00000000ff980000, 0x00000000ff980000)
  object space 13824K, 91% used [0x00000000fec00000,0x00000000ff850030,0x00000000ff980000)
 Metaspace       used 3303K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 359K, capacity 388K, committed 512K, reserved 1048576K

Process finished with exit code 0

特点:只要发生垃圾回收,就会被回收,但不是把堆中所有弱引用全部回收,回收到足够放下接下来的数据即可。
fullGC才会将所有的弱引用进行垃圾回收

虚引用

虚引用和终结器引用必须配合引用队列。如下:

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

  • 虚引用的一个体现是释放直接内存所分配的内存,当引用的对象ByteBuffer被垃圾回收以后,虚引用对象Cleaner就会被放入引用队列中,然后调用Cleaner的clean方法来释放直接内存

如下图,B对象不再引用ByteBuffer对象,ByteBuffer就会被回收。但是直接内存中的内存还未被回收。

由ReferenceHandler后台线程检查引用队列,是否有虚引用Cleaner,然后来调用Cleaner的clean方法调用freeMemory来释放内存。

终结器引用

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

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

这里重写了finallize方法,A4对象执行垃圾回收时并不是立马被回收的,因为重写了finallize方法,所以虚拟机会自动创建一个终结器引用,执行A4对象的回收时,会将终结器引用放入引用队列中,然后由一个较低优先级的线程finalizeHandler,去查看引用队列,根据终结器引用调用重写了的finallize方法,这个时候A4对象才真正被回收。
不是直接回收的,需要先放到引用队列,并且执行回收的线程优先级很低,所以fianlize方法的对象迟迟得不到回收,造成内存泄露。
不推荐finalize方法释放资源。

引用队列
  • 软引用和弱引用可以配合引用队列
    • 在弱引用和虚引用所引用的对象被回收以后,会将这些引用放入引用队列中,方便一起回收这些软/弱引用对象
  • 虚引用和终结器引用必须配合引用队列
    • 虚引用和终结器引用在使用时会关联一个引用队列
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/949053.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号