持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
在 jdk 1.2 及其以后,引入了强引用、软引用、弱引用、虚引用这四个概念。
这里先介绍一下Java种的Reference类,它位于java.lang.ref包中,对于软引用、弱引用、虚引用这三种引用都有对应的类,并且这些类都是继承自Reference类。
强引用在java.lang.ref里没有实际的对应类型。
1、强引用(StrongReference)
强引用不会被GC回收,并且在java.lang.ref里也没有实际的对应类型。举个例子来说:
Object obj = new Object();
这里的obj引用便是一个强引用,不会被GC回收。
强引用有如下特点:
- 强引用可以直接访问目标对象
- 强引用(存在)指向的对象任何时候都不会被回收,JVM宁愿抛出OOM(out of memory)异常,也不会回收。
- 强引用可能会导致内存泄漏(memory leak)
ps:上面第二点第三点提到的OOM和内存泄漏(memory leak)是两个概念:
- 内存溢出(out of memory) 是指 程序在申请内存时,没有足够的内存空间供其使用,出现 out of memory.
- 内存泄漏(memory leak) 是指 程序申请内存后,无法释放已申请的内存空间,这样的泄漏积少成多,memory leak 会导致 out of memory .
注意:为了尽量避免浪费内存的情况,我们有时可以在变量sb不再使用后通过显示的将变量sb置为null(sb = null),来加速对象的回收。
2、软引用(SoftReference)
当有引用指向某个obj的时候,通常发生GC的时候不会把这个对象处理掉,但是被软引用包装的对象,当应用内存将要被耗尽的时候-->即将发生OOM,如果此时没有强引用同时指向它,垃圾处理器就会把它带走。这么看来,软应用的生命周期还是很长的,正是由于这种特性软引用在caching(缓存)和pooling(池)中用处广泛。
软引用的用法:
Object obj = new Object();
SoftReference softRef = new SoftReference<Object>(obj);
//删除强引用,如果这里不删除强引用,之后内存满了,GC的时候也不会回收软引用
obj = null;
//调用gc
System.gc();
System.out.println("gc之后的值:" + softRef.get()); // 对象依然存在
3、弱引用(WeakReference)
弱引用中的对象具有很短的声明周期,因为在系统GC时,只要发现弱引用,如果此时没有强引用同时指向它,不管堆空间是否足够,都将会释放WeakReference所引用的对象,将对象进行回收。
弱引用的简单使用:
Object obj = new Object();
WeakReference weakRef = new WeakReference<Object>(obj);
//删除强引用,如果这里不删除强引用,GC的时候不会回收弱引用
obj = null;
System.out.println("gc之后的值:" + weakRef.get()); // 对象依然存在
//调用gc
System.gc();
System.out.println("gc之后的值:" + weakRef.get()); // 对象为null
弱引用的作用:
软引用和弱引用都非常适合保存那些可有可无的缓存数据,当内存不足时,缓存数据被回收(再通过备选方案查询),当内存充足时,也可以存在较长时间,起到加速的作用。
对于弱引用使用的场景,在ThreadLocal中有使用(ThreadLocal中使用了set进行赋值之后,之后我们不需要在get它使用了,大家务必使用remove方法,将ThreadLocalMap中的Entry删除;不然当tl和ThreadLocalMap中key同时指向的ThreadLocal对象被回收了,那么此时ThreadLocalMap的key会为null,此时ThreadLocalMap中的键值对就会变成null->value了,如果这个线程不退出,他就会一直存在内存中,这样可能会造成内存泄漏),下图为马老师的ThreadLocal图:
4.虚引用(PhantomReference)
虚引用 就是 形同虚设 ,它并不能决定 对象的生命周期。任何时候这个只有虚引用的对象都有可能被回收。因此,虚引用主要用来跟踪对象的回收,清理被销毁对象的相关资源。PhantomReference的 get() 方法永远返回null ,而且只提供了与引用队列同用的构造函数(且只有这一个构造方法)。所以虚引用必须和引用队列一同使用。
Object obj = new Object();
ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
PhantomReference<Object> phanRef = new PhantomReference<Object>(obj, refQueue);
// 调用phanRef.get()不管在什么情况下会一直返回null
Object objg = phanRef.get();
// 如果obj被置为null,当GC发现了虚引用,GC会将phanRef插入进我们之前创建时传入的refQueue队列
// 注意,此时phanRef所引用的obj对象,并没有被GC回收,在我们显式地调用refQueue.poll返回phanRef之后
// 当GC第二次发现虚引用,而此时JVM将phanRef插入到refQueue会插入失败,此时GC才会对obj进行回收
Reference<? extends Object> phanRefP = refQueue.poll();
虚引用的作用:
Phantom references are most often used to schedule post-mortem cleanup actions.
管理直接内存(堆外的内存,不归GC垃圾回收器管理),但是我们的虚引用可以管理直接内存。(对于直接引用,可以参考零拷贝技术,弄清楚了零拷贝,对于直接内存大家都应该清楚了)。
对于虚引用,我们很少使用得到,一般都是JVM去回收垃圾堆外内存的,因为GC回收不到。
Author:YuShiwen
于稀土掘金
2022.5.26