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

Java中的四种引用

程序员文章站 2022-07-10 18:32:29
Java中的四种引用强引用软引用弱引用虚引用# Java中的四种引用Java中的四种引用Java中为什么要有设计这四种引用强引用软引用弱引用虚引用Java中为什么要设计这四种引用Java的内存分配和内存回收,都不需要程序员自己负责的,都是由虚拟机负责,而虚拟机判断一个对象是否要回收,就是看是否有引用指向此对象,也就是用可行性分析强引用软引用弱引用虚引用# Java中的四种引用Java中的四种引用Java中有四种引用类型:强引用、软引用、弱引用、强引用Java中为什么要有设计这四种引...

Java中的四种引用

Java中有四种引用类型:强引用、软引用、弱引用、强引用

Java中为什么要有设计这四种引用

Java的内存分配和内存回收,都不需要程序员自己负责的,都是由虚拟机负责,而虚拟机判断一个对象是否要回收,就是看是否有引用指向此对象,也就是用可行性分析

设计四种引用的目的有两个:

  1. 可以让程序员通过代码的方式决定某个对象的生命周期
  2. 有利于使用垃圾回收

强引用

强引用的表现:

Object o = new Object();

像这种主动去new一个对象的引用,就是强引用,这种对象永远不会被回收,即使内存不知,JVM只会爆OOM,也不是去回收

如果想要它被回收可以主动把强引用和对象的关联中断,也就是断掉指针

o = null

可以通过手动调用GC,看看指针断裂之后,资源会不会被回收,为了方便观察回收的情况,我这里重新写一个类,重写finalize方法

public class TestQuote {
    @Override
    protected void finalize() throws Throwable{
        System.out.println("TestQuote 被回收了");
    }
    public static void main(String[] args) {
        TestQuote testQuote = new TestQuote();
        testQuote = null;
        System.gc();
    }
}

运行结果:
Java中的四种引用

软引用

先看如果创建一个软引用:

软引用就是把对象用SoftReference包裹一下,当我们需要从软引用对象获得包裹的对象,只需要get一下就好了

SoftReference<TestQuote> testQuoteSoftReference = new SoftReference<>(new TestQuote());
TestQuote testQuote = testQuoteSoftReference.get();
System.out.println(testQuote);

软引用的特点是:

当内存不足,会触发JVM的GC,如果GC后,内存还是不足,就会把软引用包裹的对象给清除,也就是当JVM的内存不足的时候,才会回收该对象。

下面模拟一下内存不足的清除状况

SoftReference<byte[]> softReference = new SoftReference<byte[]>(new byte[1024*1024*10]);
        System.out.println(softReference.get());
        System.gc();
        System.out.println(softReference.get());
        byte[] bytes = new byte[1024*1024*10];
        System.out.println(softReference.get());

这里我定义了一个软引用对象,里面包裹了byte[],byte[]占用了10M,然后又创建了10M的byte[]

然后我们需要改一个参数:

-Xmz20M

代表最大的堆内存为20M

运行结果:

Java中的四种引用

可以很清楚看到手动完成GC后,软引用对象包裹的byte[]还活得好好的,但是当再创建一个10M的byte[]后,最大堆内存不够用,所以把软引用对象包裹的byte[]给清除了,如果不清除,就会抛出OOM

软引用比较合适用作缓存,当内存足够的时候,可以正常的拿到缓存,当内存不够,就会先干掉缓存,不至于马上抛出OOM

弱引用

弱引用的使用和软引用类型,只是关键字变成了WeakReference:

WeakReference<byte[]> weakReference = new WeakReference<>(new byte[1024*1024*10]);
		System.out.println(weakReference.get());

弱引用的特点是不管内存是否足够,只要发生GC,都会被回收:

WeakReference<byte[]> weakReference = new WeakReference<>(new byte[1024*1024*10]);
        System.out.println(weakReference.get());
        System.gc();
        System.out.println(weakReference.get());

运行结果:
Java中的四种引用

可以看到内存还是很足够的时候,但是触发了GC,资源还是内回收了

弱引用在很多地方都有用到,比如ThreadLocal、WeakHashMap

虚引用

虚引用又被称为幻影引用

ReferenceQueue queue = new ReferenceQueue();
        PhantomReference<byte[]> reference = new PhantomReference<>(new byte[1],queue);
        System.out.println(reference.get());

虚引用的使用和上面的软引用和弱引用的区别还是挺大了,这里先不管ReferenceQueue是什么东西

直接运行

Java中的四种引用

直接输出了null,这里先去看看get方法的源码:
Java中的四种引用

直接返回了null,这就是虚引用的特点之一:无法通过虚引用来获取对一个对象的真实引用。

那虚引用的使用有什么意义,这里先看回创建虚引用的代码

ReferenceQueue queue = new ReferenceQueue();
        PhantomReference<byte[]> reference = new PhantomReference<>(new byte[1],queue);
        System.out.println(reference.get());

创建虚引用对象,这里除了把包裹对象传进去,还传进了一个ReferenceQueue,从名字就可以看出它是一个队列。

虚引用的特点之二就是虚引用必须与ReferenceQueue一起使用,当GC准备回收一个对象,如果发现它还有虚引用就会再回收之前,把这个虚引用加入到与之关联的ReferenceQueue中

接下来实践一下:

ReferenceQueue queue = new ReferenceQueue();
        List<byte[]> bytes = new ArrayList<>();
        PhantomReference<TestQuote> reference = new PhantomReference<TestQuote>(new TestQuote(),queue);
        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                bytes.add(new byte[1024*1024*1024*1024*1024*1024*1024]);
            }
        }).start();

        new Thread(() -> {
            while (true) {
                Reference poll = queue.poll();
                if (poll != null) {
                    System.out.println("虚引用被回收了:" + poll);
                }
            }
        }).start();
        Scanner scanner = new Scanner(System.in);
        scanner.hasNext();

运行结果

Java中的四种引用

第一个线程往集合里面塞数据,随着数据越来越多,肯定会发生GC。
第二个线程死循环,从queue里面拿数据,如果拿出来的数据不是null,就打印出来。

从运行结果可以看到:当发生GC,虚引用就会被回收,并且会把回收的通知放到ReferenceQueue中。

运行结果

[外链图片转存中…(img-kVGMRh0s-1596903475719)]

第一个线程往集合里面塞数据,随着数据越来越多,肯定会发生GC。
第二个线程死循环,从queue里面拿数据,如果拿出来的数据不是null,就打印出来。

从运行结果可以看到:当发生GC,虚引用就会被回收,并且会把回收的通知放到ReferenceQueue中。

NIO中,就运用了虚引用管理堆外内存

本文地址:https://blog.csdn.net/sclieshare/article/details/107888502

相关标签: 笔记