java-引用
概述
引用概括起来就是 强软弱虚
了解Java中的软引用、弱引用、虚引用的适用场景以及释放机制
- 强引用是我们 new 出来生成的那种,生命周期跟随着垃圾回收器
- 软引用(SoftReference) , 当内存不足的时候就会给回收, 具体的回收的条件有两个:(1)距离上次gc后首次调用的时间间隙 (2)JVM堆大小是否快到上次使用的大小容量了
- 弱引用 (WeakReference) ,场景例如: ThreadLocal , 无论如何都会回收 ,底层原理是该引用会断开对象的指针,即是使 引用为 null (都为null 了, 下次gc 肯定就会被回收了)
- 虚引用 (PhantomReference) ,场景例如: DirectByteBuffer , 没实际意义, 虚引用并不会决定对象的生命周期。主要用来跟踪对象被垃圾回收器回收的活动。
问题
- 都说软引用 (SoftReference) 在内存不足的时候就会回收 , 那么什么时候叫内存不足呢?
- ThreadLocal 里使用了 weakReference , 这是为什么
源码分析
引用回收流程
源码分析
public abstract class Reference<T> {
// 第一部分
//===================================================
//引用的对象
private T referent;
//===================================================
// 第二部分
//===================================================
//回收队列,由使用者在Reference的构造函数中指定
volatile ReferenceQueue<? super T> queue;
//当该引用被加入到queue中的时候,该字段被设置为queue中的下一个元素,以形成链表结构
volatile Reference next;
//在GC时,JVM底层会维护一个叫DiscoveredList的链表,存放的是Reference对象,discovered字段指向的就是链表中的下一个元素,由JVM设置
transient private Reference<T> discovered;
//等待加入queue的Reference对象,在GC时由JVM设置,会有一个java层的线程(ReferenceHandler)源源不断的从pending中提取元素加入到queue
private static Reference<Object> pending = null;
//===================================================
// 第三部分
//===================================================
//进行线程同步的锁对象
static private class Lock { }
private static Lock lock = new Lock();
//===================================================
}
强引用
Object o = new Object();
软引用(SoftReference)
弱引用 (WeakReference)
ThreadLocal
ThreadLocal 底层是使用了 Thread 对象的内部静态类ThreadLocalMap来存储键值对的, 而 ThreadLocalMap 的键值对对象可以看到是继承自 WeakReference
// java.lang.ThreadLocal#get
public T get() {
// 获取到线程
Thread t = Thread.currentThread();
// 其实就是对 Thread 的变量 ThreadLocalMap , 进行操作
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
//java.lang.ThreadLocal#getMap
ThreadLocalMap getMap(Thread t) {
// 拿的是 Thread 的变量
return t.threadLocals;
}
然后我们再看一下
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
WeakReference 继承自Reference , 这个类我们在上面的源码分析讲过 , 它里面有一个重要的成员变量 reference
, 这个变量会和 gc 的时候有关 , 也就是引用 , 假如一个对象没了引用 , 毫无疑问肯定就会给回收了 .
我们以一个例子来学习 ThreadLocal 使用 WeakReference 的原理
public class ThreadLocalDemo {
public static void main(String[] args) throws InterruptedException {
firstStack();
// System.gc();
Thread.sleep(1000);
Thread thread = Thread.currentThread();
System.out.println(thread); // 在这里打断点,观察thread对象里的ThreadLocalMap数据
}
// 通过是否获取返回值观察A对象里的local对象是否被回收
private static A firstStack(){
A a = new A();
System.out.println("value: "+ a.get());
return a;
}
private static class A{
private ThreadLocal<String> local = ThreadLocal.withInitial(() -> "in class A");
public String get(){
return local.get();
}
public void set(String str){
local.set(str);
}
}
}
我们先对 System.gc();
这一句注释掉 , 然后打下短点 , 得到下面的堆栈信息 , 看到 ThreadLocal 相关的信息 , 可以看到 , 此时 reference
还是有值的.
假如我们此时放开注释
虚引用 (PhantomReference)
public class Cleaner extends PhantomReference<Object> {
...
}