深入理解Java四大引用

java有四种引用类型分别是:强引用、软引用、弱引用、虚引用

四大引用设计作用

​ 由于java的内存分配和内存回收,都是由JVM去负责。一个对象是否可以被回收,主要看是否有引用指向此对象,即可达性分析。由于我们程序员一般不去主动的调用GC方法,要想达到对某个对象的生命周期进行管理,就要灵活应用各种引用来创建对象。

  • 设计目的:

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

强引用

使用最广泛的,也是最常见的。

Object o = new Object();

只要某个对象有强引用与之关联,这个对象就永远不会被回收,即使内存不足,JVM也只会抛出OOM也不去回收。

但是要是想让JVM去回收该对象,那么需要中断强引用和对象之间的关联。

  • 中断关联
n=null;

为了验证,在此我们手动调用GC,看看强引用和对象之间的关联被中断,资源会不会被回收。

public class Student {
    @Override
    protected void finalize()throws Throwable{
        System.out.println(" Student 被回收了");
    }

    public static void main(String[] args) {
        Student student = new Student();
        System.gc();
        System.out.println("===============");
        student = null;
        System.gc();
    }
}
  • 结果:

Student 被回收了

  • 注意

    • 开发中不要重写finalize方法
    • 开发中可以将一些对象手动赋值为NULL,可以提醒JVM将这些资源进行垃圾回收

软引用

  • 创建一个软引用
SoftReference<Student> studentSoftReference = new SoftReference<Student>(new Student());
  • 软引用就是把对象用SoftReference包裹一下,当我们需要从软引用对象获取包裹的对象,只要get一下就可以。
SoftReference<Student> studentSoftReference = new SoftReference<Student>(new Student());
        Student student = studentSoftReference.get();
        System.out.println(student);
  • 特点:

    • 当内存不足时,会触发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());
  • 运行时需要加上JVM参数:-Xmx20M 代表最大的堆内存为20M
  • 结果

[B@452b3a41

[B@452b3a41

null

  • 当我们手动GC后,软引用对象包裹中的对象还没事,当我们创建一个10M的byte后,最大堆内存不够用了,就将软引用的对象给垃圾回收了。
  • 作用:可以当做缓存,当内存足够的时候可以正常拿到缓存,当内存不够就会先干掉缓存,不至于出现OOM
import java.lang.ref.SoftReference;
import java.util.HashMap;

/**
 * SoftRefenceCache
 * @param <K> key的类型.
 * @param <V> value的类型.
 */
public class SoftReferenceCache<K, V> {
    private final HashMap<K, SoftReference<V>> mCache;

    public SoftReferenceCache() {
        mCache = new HashMap<K, SoftReference<V>>();
    }

    /**
     * 将对象放进缓存中,这个对象可以在GC发生时被回收
     *
     * @param key key的值.
     * @param value value的值型.
     */

    public void put(K key, V value) {
        mCache.put(key, new SoftReference<V>(value));
    }

    /**
     * 从缓存中获取value
     *
     * @param key
     *
     * @return 如果找到的话返回value,如果被回收或者压根儿没有就返* 回null
     */

    public V get(K key) {
        V value = null;

        SoftReference<V> reference = mCache.get(key);

        if (reference != null) {
            value = reference.get();
        }

        return value;
    }

    public static void main(String[] args) {
        SoftReferenceCache<Integer, String> mPersonCache = new SoftReferenceCache<Integer, String>();

        mPersonCache.put(0, ("zhong"));
        mPersonCache.put(1, ("hu"));
       

        // 去拿zhong
        String p = (String) mPersonCache.get(1);
    }
}

弱引用

弱引用和软引用类似,只是关键字换成了WeakReferce

 WeakReference weakReference = new WeakReference<byte[]>(new byte[1024*1024*10]);
        System.out.println(weakReference.get());
  • 特点:不管内存是否够用,只要发生GC就会被回收(这也是与软引用之间最明显的区别)
  • 用途:ThreadLocal、WeakHashMap。

虚引用

虚引用又称为幻影引用

 ReferenceQueue queue = new ReferenceQueue();
        PhantomReference<byte[]> reference = new PhantomReference<byte[]>(new byte[1],queue);
        System.out.println(reference.get());
  • 虚引用的使用和其他的还是有较大的区别。
  • 结果:

null

  • 因为其get方法直接返回了null
  • 创建虚引用对象,我们除了把包裹的对象传了进去,还传了一个ReferenceQueue(队列)
  • 虚引用必须与ReferenceQueue一起使用,当GC准备回收一个对象,如果发现它还有虚引用,就会在回收之前,把这个虚引用加入到与之关联的ReferenceQueue中。
 ReferenceQueue queue = new ReferenceQueue();
        List<byte[]> bytes = new ArrayList<>();
        PhantomReference<Student> reference = new PhantomReference<Student>(new Student(),queue);
        new Thread(() -> {
            for (int i = 0; i < 100;i++ ) {
                bytes.add(new byte[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.lang.ref.PhantomReference@5ac42552

第一个线程往集合中放数据,随着数据越来越多,肯定发送GC

第二个线程死循环,从queue中拿数据,如果拿出来的数据不是null那就打印出来

当发生GC的时候,虚引用就会被回收,并且会把回收的同志放到ReferenceQueue中

在NIO中,可以使用虚引用来管理堆外内存

最后

  • 如果觉得看完有收获,希望能给我点个赞,这将会是我更新的最大动力,感谢各位的支持
  • 欢迎各位关注我的公众号【java冢狐】,专注于java和计算机基础知识,保证让你看完有所收获,不信你打我
  • 如果看完有不同的意见或者建议,欢迎多多评论一起交流。感谢各位的支持以及厚爱。

image

猜你喜欢

转载自blog.csdn.net/issunmingzhi/article/details/110819911
今日推荐