文章目录
- 一、 Java 引用类型 ( 强、软、弱、虚 )
- 二、 软引用代码示例
- 三、 弱引用代码示例
强引用 ( 不回收 ) > 软引用 ( OOM 前回收 ) > 弱引用 ( GC 必回收 ) > 虚引用 ( 回收前通知 )
如果单纯的想避免 OOM , 可以使用软引用 ;
如果对性能要求很高 , 要最大限度节省内存 , 所有的内存回收都要及时处理 , 就使用弱引用 ;
内存泄漏原理 : 长生命周期对象 , 持有短生命周期对象的引用 , 并且是强引用持有 , GC 无法释放该短生命周期对象引用 , 造成 OOM ;
一、 Java 引用类型 ( 强、软、弱、虚 )
1. 引用 :
① 变量引用 : 创建对象 , 将对象赋值给变量
, 那么变量
就是对象的引用 ;
② 对象引用 : 对象
可以调用对象
, 那么对象
持有对象
的引用 ;
2. 引用类型 : GC 垃圾回收机制 与 引用类型 密切相关 ;
① 强引用 : 最普遍的引用 , Object object = new Object() , 这就是强引用 ;
② 软引用 : 用于定义一些有用 , 但不是必须的对象 , 对象被软引用引用 , 当系统内存严重不足 , 在报出 OutOfMemoryError 错误之前就会将软件用释放掉 , 如果将软引用释放掉之后 , 还是内存不足 , 就会报 OutOfMemoryError 异常 ;
③ 弱引用 : 弱引用也是描述非必须对象 , 该引用的引用强度比软引用更弱 , 该引用对象 , 生命周期只到下一次 GC 回收之前 , GC 只要扫描到了弱引用 , 直接回收 , 即使内存足够 , 也要回收该对象所占用的内存 ;
④ 虚引用 : 虚引用无法获得被引用的对象 , 其唯一的作用是对象被回收时 , 可以得到相应的通知 ; 虚引用不会对对象的生存周期造成影响任何 , GC 忽略虚引用 , 即使有虚引用引用某对象 , GC 会当做该引用不存在 ; 开发时不常用 , 一般开发底层 SDK , 或监测系统运行类的软件时 , 才使用 ;
强引用 ( 不回收 ) > 软引用 ( OOM 前回收 ) > 弱引用 ( GC 必回收 ) > 虚引用 ( 回收前通知 )
二、 软引用代码示例
1. 软引用创建 :
① 直接创建软引用 : 在软件用构造函数中创建一个软引用 , 不涉及外部引用 ;
代码语言:javascript复制SoftReference<Object> softReference3 = new SoftReference<>(new Object());
② 传入创建好的对象引用创建软引用 : 软引用是通过在 软引用构造函数 传入引用对象创建的 , 首先要创建引用对象 ; 下面的强引用可以在创建软引用完毕后置空解除引用 , 否则肯定不能被回收 ;
代码语言:javascript复制Object softObject = new Object();
SoftReference<Object> softReference1 = new SoftReference<>(softObject);
③ 创建软引用并设置引用队列 : 在上述基础上 , 可以在构造函数中设置一个引用队列参数 ;
代码语言:javascript复制Object softObject = new Object();
// 引用队列
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// 创建软引用, 并将该软引用引用放入引用队列中
SoftReference<Object> softReference2 = new SoftReference<>(softObject, referenceQueue);
④ 上述软引用释放 : 创建的软引用在两种情况下才会释放 :
- 软引用置空 : GC Root 不可达时回收 ;
- 内存不足 : 内存不足 , OOM 之前回收 ;
2. 软引用代码示例 :
代码语言:javascript复制import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
public class Main {
public static void main(String[] args) throws InterruptedException {
/*
软引用示例
1. 创建软引用 : public SoftReference(T referent)
2. 创建软引用并放入引用队列中 :
public SoftReference(T referent, ReferenceQueue<? super T> q)
回收时机 : 软引用在内存不足时才会被回收, OOM 的前一刻
*/
Object softObject = new Object();
// 1. 直接创建软引用
SoftReference<Object> softReference1 = new SoftReference<>(softObject);
// 2. 创建软引用并放入引用队列中
// 引用队列
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// 创建软引用, 并将该软引用引用放入引用队列中
SoftReference<Object> softReference2 = new SoftReference<>(softObject, referenceQueue);
// 3. 直接创建软件用
SoftReference<Object> softReference3 = new SoftReference<>(new Object());
//获取软引用, 调用软引用的 get() 方法 , 即可获取软引用
softReference1.get();
softReference2.get();
// 打印软件用
System.out.println("softReference1 : " softReference1.get());
System.out.println("softReference2 : " softReference2.get());
System.out.println("softReference3 : " softReference3.get());
// 软件用被回收之后, 可以通过调用 referenceQueue.poll() 方法, 获取到被回收的引用
// 如果获取到 null, 说明该软引用没有被回收
// 如果该软件用被回收, 可以获取非空值
System.out.println("referenceQueue.poll : " referenceQueue.poll());
// 验证回收机制, 将软引用对象设置为空
// 其对应的软引用对象 SoftReference<Object> 回收的时机
// - 1. 置空访问不可达时回收
// - 2. 内存不足 OOM 之前回收
softObject = null;
// 申请进行垃圾回收, 这里只是申请垃圾回收, 并不会马上调用, 大约几秒内会进行垃圾回收
System.gc();
Thread.sleep(5_000);
// 打印相关变量验证回收机制
System.out.println("");
System.out.println("softReference1 : " softReference1.get());
System.out.println("softReference2 : " softReference2.get());
System.out.println("softReference3 : " softReference3.get());
// 软件用被回收之后, 可以通过调用 referenceQueue.poll() 方法, 获取到被回收的引用
// 如果获取到 null, 说明该软引用没有被回收
// 如果该软件用被回收, 可以获取非空值
System.out.println("referenceQueue.poll : " referenceQueue.poll());
}
}
执行结果 :
代码语言:javascript复制softReference1 : java.lang.Object@1b6d3586
softReference2 : java.lang.Object@1b6d3586
softReference3 : java.lang.Object@4554617c
referenceQueue.poll : null
softReference1 : java.lang.Object@1b6d3586
softReference2 : java.lang.Object@1b6d3586
softReference3 : java.lang.Object@4554617c
referenceQueue.poll : null
三、 弱引用代码示例
1. 弱引用创建 :
① 直接创建弱引用 : 在软件用构造函数中创建一个软引用 , 不涉及外部引用 ;
代码语言:javascript复制WeakReference<Object> weakReference3 = new WeakReference<>(new Object());
② 传入创建好的对象引用创建弱引用 : 弱引用是通过在 弱引用构造函数 传入引用对象创建的 , 首先要创建引用对象 ; 下面的强引用可以在创建软引用完毕后置空解除引用 , 否则肯定不能被回收 ;
代码语言:javascript复制Object softObject = new Object();
WeakReference<Object> WeakReference1 = new WeakReference<>(weakObject);
③ 创建弱引用并设置引用队列 : 在上述基础上 , 可以在构造函数中设置一个引用队列参数 ;
代码语言:javascript复制Object softObject = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// 创建弱引用, 并将该弱引用引用放入引用队列中
WeakReference<Object> weakReference2 = new WeakReference<>(weakObject, referenceQueue);
④ 上述软引用释放 : 弱引用不管是否置空 , 只要遇到 GC , 一律回收
2. 软引用代码示例 :
代码语言:javascript复制import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
public class Main {
public static void main(String[] args) throws InterruptedException {
/*
弱引用示例
1. 创建弱引用 : public WeakReference(T referent)
2. 创建弱引用并放入引用队列中 :
public WeakReference(T referent, ReferenceQueue<? super T> q)
回收时机 : 弱引用在 GC 时被回收, 不管内存是否足够
*/
Object weakObject = new Object();
// 1. 直接创建弱引用
WeakReference<Object> WeakReference1 = new WeakReference<>(weakObject);
// 2. 创建弱引用并放入引用队列中
// 引用队列
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// 创建弱引用, 并将该弱引用引用放入引用队列中
WeakReference<Object> weakReference2 = new WeakReference<>(weakObject, referenceQueue);
// 3. 直接创建弱引用
WeakReference<Object> weakReference3 = new WeakReference<>(new Object());
//获取弱引用, 调用弱引用的 get() 方法 , 即可获取弱引用
WeakReference1.get();
weakReference2.get();
// 打印软件用
System.out.println("WeakReference1 : " WeakReference1.get());
System.out.println("WeakReference2 : " weakReference2.get());
System.out.println("WeakReference3 : " weakReference3.get());
// 弱引用被回收之后, 可以通过调用 referenceQueue.poll() 方法, 获取到被回收的引用
// 如果获取到 null, 说明该弱引用没有被回收
// 如果该软件用被回收, 可以获取非空值
System.out.println("referenceQueue.poll : " referenceQueue.poll());
// 验证回收机制, 将弱引用对象设置为空
// 这里弱引用不管是否置空 , 只要遇到 GC , 一律回收
weakObject = null;
// 申请进行垃圾回收, 这里只是申请垃圾回收, 并不会马上调用, 大约几秒内会进行垃圾回收
System.gc();
Thread.sleep(5_000);
// 打印相关变量验证回收机制
System.out.println("");
System.out.println("WeakReference1 : " WeakReference1.get());
System.out.println("WeakReference2 : " weakReference2.get());
System.out.println("WeakReference3 : " weakReference3.get());
// 软件用被回收之后, 可以通过调用 referenceQueue.poll() 方法, 获取到被回收的引用
// 如果获取到 null, 说明该弱引用没有被回收
// 如果该软件用被回收, 可以获取非空值
System.out.println("referenceQueue.poll : " referenceQueue.poll());
}
}
执行结果 :
代码语言:javascript复制WeakReference1 : java.lang.Object@1b6d3586
WeakReference2 : java.lang.Object@1b6d3586
WeakReference3 : java.lang.Object@4554617c
referenceQueue.poll : null
WeakReference1 : null
WeakReference2 : null
WeakReference3 : null
referenceQueue.poll : java.lang.ref.WeakReference@74a14482