Java强软弱虚引用Reference
本文目的:深入理解Reference
本文定位:学习笔记
学习过程记录,加深理解,提升文字组合表达能力。也希望能给学习Reference的同学一些灵感
源码说明
源码基于jdk1.8.0_111:
- ReferenceQueue 源码160行左右
- Reference 源码300行左右
- SoftReference、WeakReference、PhantomReference继承于Reference,代码量都极少
由代码量上也可以初步判断,Java引用,并不太复杂
java.lang.ref包
Package java.lang.ref:
提供引用对象类,它支持与垃圾收集器进行有限程度的交互。
Provides reference-object classes, which support a limited degree of interaction with the garbage collector. --oracle.com
java.lang.ref包中提供了几个类:SoftReference类、WeakReference类和PhantomReference类,它们分别代表软引用、弱引用和虚引用。ReferenceQueue类表示引用队列,它可以和这三种引用类联合使用,以便跟踪Java虚拟机回收所引用的对象的活动。
强引用
- 强引用可以直接访问目标对象。
- 强引用所指向的对象在任何时候都不会被系统回收。JVM宁愿抛出OOM异常,也不会回收强引用所指向的对象。
- 强引用可能导致内存泄露。
软引用SoftReference
SoftReference:软引用–>当虚拟机内存不足时,将会回收它指向的对象;需要获取对象时,可以调用get方法。
可以通过java.lang.ref.SoftReference使用软引用。一个持有软引用的对象,不会被JVM很快回收,JVM会根据当前堆的使用情况来判断何时回收。当堆的使用率临近阈值时,才会回收软引用的对象。
软引用应用场景
例如从网络上获取图片,然后将获取的图片显示的同时,通过软引用缓存起来。当下次再去网络上获取图片时,首先会检查要获取的图片缓存中是否存在,若存在,直接取出来,不需要再去网络上获取。
注意避免软引用获取对象为null
在垃圾回收器对这个Java对象回收前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该Java对象之后,get方法将返回null。所以在获取软引用对象的代码中,一定要判断是否为null,以免出现NullPointerException异常导致应用崩溃。
软引用SoftReference源码
public class SoftReference<T> extends Reference<T> {
/**
* Timestamp clock, updated by the garbage collector
*/
static private long clock;
/**
* Timestamp updated by each invocation of the get method. The VM may use
* this field when selecting soft references to be cleared, but it is not
* required to do so.
*/
private long timestamp;
/**
* Creates a new soft reference that refers to the given object. The new
* reference is not registered with any queue.
*
* @param referent object the new soft reference will refer to
*/
public SoftReference(T referent) {
super(referent);
this.timestamp = clock;
}
/**
* Creates a new soft reference that refers to the given object and is
* registered with the given queue.
*
* @param referent object the new soft reference will refer to
* @param q the queue with which the reference is to be registered,
* or <tt>null</tt> if registration is not required
*
*/
public SoftReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
this.timestamp = clock;
}
/**
* Returns this reference object's referent. If this reference object has
* been cleared, either by the program or by the garbage collector, then
* this method returns <code>null</code>.
*
* @return The object to which this reference refers, or
* <code>null</code> if this reference object has been cleared
*/
public T get() {
T o = super.get();
if (o != null && this.timestamp != clock)
this.timestamp = clock;
return o;
}
}
可以看到SoftReference有一个类变量clock和一个变量timestamp,这两个参数对于SoftReference至关重要。
- clock:记录了上一次GC的时间。这个变量由GC(garbage collector)来改变。
- timestamp:记录对象被访问(get函数)时最近一次GC的时间。
那么这两个参数有什么用?
- 我们知道软引用是当内存不足时可以回收的。但是这只是大致情况,实际上软应用的回收有一个条件:
- clock - timestamp <= free_heap * ms_per_mb
- free_heap是JVM Heap的空闲大小,单位是MB
- ms_per_mb单位是毫秒,是每MB空闲允许保留软引用的时间。Sun JVM可以通过参数-XX:SoftRefLRUPolicyMSPerMB进行设置
举个例子:
- 目前有3MB的空闲,ms_per_mb为1000,这时如果clock和timestamp分别为5000和2000,那么
- 5000 - 2000 <= 3 * 1000
- 条件成立,则该次GC不对该软引用进行回收。
- 所以每次GC时,通过上面的条件去判断软应用是否可以回收并进行回收,即我们通常说的内存不足时被回收。
弱引用WeakReference
如果一个对象只具有弱引用,那么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
应用场景示例
注意避免软引用获取对象为null
同软引用SoftReference
弱引用WeakReference源码
public class WeakReference<T> extends Reference<T> {
/**
* Creates a new weak reference that refers to the given object. The new
* reference is not registered with any queue.
*
* @param referent object the new weak reference will refer to
*/
public WeakReference(T referent) {
super(referent);
}
/**
* Creates a new weak reference that refers to the given object and is
* registered with the given queue.
*
* @param referent object the new weak reference will refer to
* @param q the queue with which the reference is to be registered,
* or <tt>null</tt> if registration is not required
*/
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
没有其他代码,GC时被回收掉。
虚引用
虚引用是所有引用类型中最弱的一个。一个持有虚引用的对象,和没有引用几乎是一样的,随时都可能被垃圾回收器回收。当试图通过虚引用的get()方法取得强引用时,总是会失败。并且,虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程。 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在垃圾回收后,销毁这个对象,奖这个虚引用加入引用队列。
使用场景
未遇到
虚引用PhantomReference源码
public class PhantomReference<T> extends Reference<T> {
/**
* Returns this reference object's referent. Because the referent of a
* phantom reference is always inaccessible, this method always returns
* <code>null</code>.
*
* @return <code>null</code>
*/
public T get() {
return null;
}
/**
* Creates a new phantom reference that refers to the given object and
* is registered with the given queue.
*
* <p> It is possible to create a phantom reference with a <tt>null</tt>
* queue, but such a reference is completely useless: Its <tt>get</tt>
* method will always return null and, since it does not have a queue, it
* will never be enqueued.
*
* @param referent the object the new phantom reference will refer to
* @param q the queue with which the reference is to be registered,
* or <tt>null</tt> if registration is not required
*/
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
- 可以看到get函数返回null,正如前面说得虚引用无法获取对象引用
- 同时可以看到虚引用只有一个构造函数,所以必须传入ReferenceQueue对象。
- 前面提到虚引用的作用是判断对象是否被回收,这个功能正是通过ReferenceQueue实现的。
ReferenceQueue
实现了链表结构的队列
当系统要回收Reference持有的对象引用referent的时候,Reference的enqueue函数会被调用,而在这个函数中调用了ReferenceQueue的enqueue函数。
Reference与引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
参考
- https://docs.oracle.com/javase/1.5.0/docs/api/java/lang/ref/package-summary.html
- https://blog.csdn.net/m0_37700275/article/details/79820814
对本文有什么建议(内容、写作风格等),欢迎留言提出,感谢!