• Java中的 WeakReference 和 SoftReference


    我们知道Java语言中没有指针,取而代之的是引用reference。Java中的引用又可以分为四种:强引用,弱引用(WeakReference),软引用(SoftReference),虚引用(PhantomReference)。其中强引用,就是我们平时使用的最多的最普通的引用,虚引用一般我们是没有机会使用到的。所以我们主要了解下 WeakReference 和 SoftReference(除了上面说的四种引用之外,其实还有一种引用——原子引用AtomicReference,用于并发编程环境)。

    1. 先上一段代码:

    public class ReferenceTest {
        public static void main(String[] args){
            LinkedList<byte[]> list = new LinkedList<>();
            for(int i=0; i<1024; i++){
                list.add(new byte[1024*1024]);
            }
        }
    }

    上面的代码会抛出:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

    堆内存溢出。因为我们不断的在堆上分配一个 1M 大小的 byte[]对象,并且将该引用加入到 list 中,循环1024次,需要占用 1G 的堆内存,从而导致 heap space OutOfMemory.

    2. 我们使用 WeekReference 对代码进行修改:

    public class ReferenceTest {
        public static void main(String[] args) {
            long beginTime = System.nanoTime();
            LinkedList<WeakReference<byte[]>> list = new LinkedList<>();
            for (int i = 0; i < 1024; i++) {
                list.add(new WeakReference<>(new byte[1024 * 1024]));
            }
            long endTime = System.nanoTime();
            System.out.println(endTime - beginTime);
        }
    }

    输出的结果:195947704 (0.19秒)

    我们发现堆内存溢出的错误没有了。这是什么原因呢。因为我们使用了 弱引用WeekReference 来引用堆上的 1M 的byte[]对象,而弱引用WeekReference引用的对象,如果仅仅只被弱引用,而没有被强引用的话,在下一次GC时,就会回收该对象占用的内存,所以不会内存溢出。

    3. 我们使用 SoftReference 对代码进行修改:

    public class ReferenceTest {
        public static void main(String[] args) {
            long beginTime = System.nanoTime();
            LinkedList<SoftReference<byte[]>> list = new LinkedList<>();
            for (int i = 0; i < 1024; i++) {
                list.add(new SoftReference<>(new byte[1024 * 1024]));
            }
            long endTime = System.nanoTime();
            System.out.println(endTime - beginTime);
        }
    }

    输出结果:1499904286 (1.5秒)

    我们发现堆内存溢出的错误也没有了。因为我们使用了 软引用SoftReference 来引用堆上的 1M 的byte[]对象,而软引用SoftReference引用的对象,如果仅仅只被软引用,而没有被强引用的话,在内存空间不足时,GC 就会回收该对象占用的内存,所以不会内存溢出。

    但是我们注意到 采用WeekReference和采用SoftReference所花费的时间,有接近10被的差距。原因应该是,SoftReference只有在内存空间不足时,GC才会回收对象占用的空间,而这时进行的是 full GC,full GC会导致 STW 程序暂停,所以花费的时间过多。

    4. 总结

    强引用:只要堆上的对象,被至少一个强引用所指向,那么GC就不会回收该对象的内存空间。

    弱引用:只要堆上的对象仅仅只被弱引用所指向,不管当前内存空间是否足够,下次GC都会回收对象的内存空间。

    软引用:只要堆上的对象仅仅只被软引用所指向,并且当内存空间不足时,GC才会回收对象的内存空间。

    WeakReference 和 SoftReference一般使用在构造一个缓存系统,比如使用一个map来构造。因为缓存系统是一个“全生命期”的对象,系统停止,缓存对象才会被销毁,所以当我们不断的想缓存对象中添加对象时,那么就会导致该缓存对象map所引用的对象越来越多,而因为是强引用,这些被放进map缓存了的对象不能被GC所回收,那么就导致系统堆内存占用会越来越大,从而最终导致内存溢出。

    那么此时我们就可以使用 WeakReference 或 SoftReference了,将强引用通过WeakReference 和 SoftReference 包装之后,变成弱引用和软引用,那么当缓存中的对象,仅仅被缓存map所引用时,那么分别在下次GC和内存不足GC时就会回收这些对象占用的内存。其实JDK给我们提供了一个专门的类:WeakHashMap ,弱引用的hashMap,所以构造缓存系统是,我们可以考虑使用它。

    其实这里引出了另外一个问题,jdk中众多的map,我们应该如何进行选择

    HashMap

    ConcurrentHashMap

    TreeMap

    WeakHashMap

    LinkedHashMap

    Collections.synchronizedMap

    Hashtable

    等等。我们在选择一个map时,应该好好的考虑下,那个更加适合我们的需求。

  • 相关阅读:
    PDF 中的对象
    关于注释
    显示简单图形
    TColor 与 RGB 的转换函数
    关于运算符
    10 进制转 2 进制、16 进制
    Pascal 语言中的关键字及保留字
    语言字符集
    Async Java HTTP client
    老梁刨楚汉
  • 原文地址:https://www.cnblogs.com/digdeep/p/4485711.html
Copyright © 2020-2023  润新知