聊聊JVM软引用和弱引用
Posted wen-pan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了聊聊JVM软引用和弱引用相关的知识,希望对你有一定的参考价值。
一、说明
由于软引用和弱引用工作流程相同,所以这里只介绍软引用!
-
软引用和弱引用最大的不同点:
- 软引用:发生了垃圾回收,并且回收后内存仍然不足,并且【
被软引用对象指向的对象
】没有强引用,那么【被软引用对象指向的对象
】就会被回收。 - 弱引用:只要发生了垃圾回收,并且【
被弱引用对象引用的对象
】没有被其他【被弱引用对象引用的对象
】就会被GC回收
- 软引用:发生了垃圾回收,并且回收后内存仍然不足,并且【
-
JVM中有四种引用,分别是
强、软、弱、虚
,千万不要将软弱虚
这三种引用理解为栈上的变量指向堆上的对象
的指针。 -
软引用、弱引用、虚引用:这三种引用其实就是一个个的类,通过对应的类名翻译过来的中文名称。
// 软引用 public class SoftReference<T> extends Reference<T> { } // 弱引用 public class WeakReference<T> extends Reference<T> { } // 虚引用 public class PhantomReference<T> extends Reference<T> { }
-
软引用和弱引用的工作原理是非常类似的,虚引用和前面两种引用出入稍大
-
软、弱、虚三种引用都可以配合队列来使用,不过软、弱两种引用可以不配合队列单独使用。而虚引用必须配合队列使用
二、软引用原理
- 发生了垃圾回收,并且回收后内存仍然不足,并且【
被软引用指向的对象
】没有强引用,那么【被软引用指向的对象
】就会被回收。
1、示例代码
// 设置JVM运行时参数:-Xmx20m 控制堆大小为20m方便演示
public static void main(String[] args) throws IOException {
// 创建A对象,在堆上分配10m的空间
A a = new A();
// a 对象是被软引用引用的对象
SoftReference<A> softReference = new SoftReference<>(a);
System.out.println("被软引用引用的对象:" + softReference.get());
// 释放a对象的强引用,如果这里不释放强引用,那么下一行再次申请内存的时候就会OOM。因为a对象有强引用无法被回收
a = null;
// 继续在堆上申请10m空间,造成内存不够用,演示 被软引用引用的对象被垃圾回收
A aa = new A();
System.out.println("被软引用引用的对象:" + softReference.get());
}
public class A {
public static int _10MB = 10 * 1024 * 1024;
byte[] bytes;
public A() {
// 分配一个10的内存
bytes = new byte[_10MB];
}
}
2、代码输出
被软引用引用的对象:org.memory.jvm.t1.reference.A@61bbe9ba
被软引用引用的对象:null
3、原理图
- 对于a对象,他被a变量强引用,被softReference对象引用。
- 一旦①处的强引用断开了,那么a对象就没有强引用了。那么当堆空间不足时(经过GC垃圾回收后仍然不足),则可以将a对象占用的空间进行回收。
这里有个问题:a对象虽然被回收了,那么softReference对象
并没有被回收,一般来说当被软引用引用的对象被回收后,这个软引用对象也就没有多大的用处了,也应该被回收掉。那么如何回收呢?那就是配合队列一起使用!!!
三、软引用配合队列一起使用
从SoftReference
的构造函数中就可以看出来,他接收一个队列参数。当【被软引用引用的对象】(a对象)被回收时,JVM会把softReference
对象放入ReferenceQueue
中。我们可以自己监听这个队列,将队列中的元素取出来,做自定义的操作。
// 接收一个队列
public SoftReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
this.timestamp = clock;
}
1、代码演示
// 设置JVM运行时参数:-Xmx20m 控制堆大小为20m方便演示
public static void main(String[] args) throws IOException {
// 创建A对象,在堆上分配10m的空间
A a = new A();
// 队列
ReferenceQueue<A> queue = new ReferenceQueue<>();
SoftReference<A> softReference = new SoftReference<>(a, queue);
System.out.println("软引用对象:" + softReference);
// 释放a对象的强引用,如果这里不释放强引用,那么下一行再次申请内存的时候就会OOM。因为a对象有强引用无法被回收
a = null;
// 继续在堆上申请10m空间,造成内存不够用,演示 被软引用引用的对象被垃圾回收
A aa = new A();
Reference<? extends A> reference = queue.poll();
System.out.println("队列中的对象:" + reference);
}
2、结果输出
软引用对象:java.lang.ref.SoftReference@61bbe9ba
队列中的对象:java.lang.ref.SoftReference@61bbe9ba
可以看到这个软引用对象确实被添加到队列中了。
四、软引用和弱引用使用场景
-
软引用和弱引用一般都可以被用于实现内存敏感的缓存
-
弱引用的使用体现在threadlocal里
- ThreadLocalMap中的entry实体就是一个弱引用
static class ThreadLocalMap { // Entry是一个弱引用 static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } }
以上是关于聊聊JVM软引用和弱引用的主要内容,如果未能解决你的问题,请参考以下文章