强引用、软引用、弱引用、幻象引用的理解
问题:
强引用、软引用、弱引用、幻象引用有什么区别?具体使用场景是什么?
知识点:
- 对象可达性
对象可达性对于我们理解JVM 可达性分析有重要作用,具体后续文章会谈到。 - 引用队列(ReferenceQueue)使用。
利用引用队列,我们可以在对象处于相应状态时,执行后期处理逻辑。例如:LeakCanary监控内存泄漏的源码中:
//源码地址 LeakCanary
com.squareup.leakcanary.RefWatcher
private void removeWeaklyReachableReferences() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
//do something
retainedKeys.remove(ref.key);
}
}
removeWeaklyReachableReferences 把已被回收的对象的 key 从 retainedKeys 移除,剩下的 key 都是未被回收的对象;LeakCanary分析可以参考文章:带你学开源项目:LeakCanary- 如何检测 Activity 是否泄漏
- Reachablity Fence
除了这四种基本引用类型,我们也可以通过底层API来达到引用的效果,这就是所谓的设置reachability fence。这里不做详细介绍,有兴趣的自己查看相关资料。
回答问题:
在Java语言中,除了基本数据类型以外,其他都是指向各类对象的对象引用,根据生命周期长短,通常分为四类:
强引用:我们正常new出来对象就是强引用,当内存不够的时候,JVM宁可抛出异常,也不会回收强引用对象。对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显示的将相应(强)引用赋值为null,就是可以被垃圾收集了,当然具体回收时机还是要看垃圾收集策略。
软引用(SoftReference):软引用生命周期比强引用低,在内存不够的时候,会进行回收软引用对象。软引用对象经常和引用队列(ReferenceQueue)一起使用,在软引用所引用的对象被GC回收后,会把该引用加入到引用队列中。通常用来实现内存敏感的缓存。例如:Android图片框架Fresco中就使用到了。
public class OOMSoftReference<T> {
SoftReference<T> softRef1;
SoftReference<T> softRef2;
SoftReference<T> softRef3;
public OOMSoftReference() {
softRef1 = null;
softRef2 = null;
softRef3 = null;
}
public void set(@Nonnull T hardReference) {
softRef1 = new SoftReference<T>(hardReference);
softRef2 = new SoftReference<T>(hardReference);
softRef3 = new SoftReference<T>(hardReference);
}
@Nullable
public T get() {
return (softRef1 == null ? null : softRef1.get());
}
public void clear() {
if (softRef1 != null) {
softRef1.clear();
softRef1 = null;
}
if (softRef2 != null) {
softRef2.clear();
softRef2 = null;
}
if (softRef3 != null) {
softRef3.clear();
softRef3 = null;
}
}
}
有兴趣可以自己去查看源码。
弱引用(WeakReference):弱引用生命周期比软引用要短,在下一次GC的时候,扫描到它所管辖的区域存在弱引用的话,不管当前内存是否够,都会进行回收。(由于垃圾回收器是一个优先级很低的线程,因此不一定会很快回收弱引用的对象)。弱引用和软引用一样,也会经常和引用队列(ReferenceQuene)一起使用,在弱引用所引用的对象被GC回收后,会把该引用加入到引用队列中。经常用在图片缓存中。例如:ThreadLocal中所持用的静态类ThreadLocalMap的Key值就用到了弱引用,防止内存泄露(value可能存在内存泄露,调用remove方法)。
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
有兴趣的话,可以查看源码,也可以参考文章:理解Java中的ThreadLocal。
幻象引用(PhantomReference):又叫虚引用,幻想引用仅仅是提供了一种确保对象被finalize后会处理某些事情,必须和引用队列一起使用(ReferenceQuene)。比如上面一篇文章所说的Cleaner机制中就使用到了幻象引用,也可以用来跟踪对象被垃圾回收器回收的活动,当一个幻象引用关联的对象被垃圾回收器回收之前收到一条系统通知。
参考:
- 带你学开源项目:LeakCanary- 如何检测 Activity 是否泄漏
- fresco部分源码
- 理解Java中的ThreadLocal
- 极客时间APP核心技术第四讲|强引用、软引用、弱引用、幻象引用有什么区别?
声明:此为原创,转载请联系作者
作者:微信公众号添加公众号-遛狗的程序员 ,或者可以扫描以下二维码关注相关技术文章。
qrcode_for_gh_1ba0785324d6_430.jpg当然喜爱技术,乐于分享的你也可以可以添加作者微信号:
WXCD.jpeg