您的浏览器不支持CSS3,建议使用Firfox、Chrome等浏览器,以取得最佳显示效果

Java引用类型简介与应用

开发技术 463℃ 0 9个月前 (03-03)

摘要

Java引用类型简介与应用。

Java引用类型

Java中有四种引用类型。其中SoftReference、WeakReference、PhantomReference构造时都可以指定ReferenceQueue,当目标对象 (Referent) 被回收时,Reference会被添加到队列中。

中文英文取得目标对象 (Referent) 方式垃圾回收条件ReferenceQueue构造参数
强引用StrongReference直接调用不回收
软引用SoftReferenceget方法gc且内存不足时回收可选参数
弱引用WeakReferenceget方法gc时回收可选参数
虚引用PhantomReference无法取得,get方法始终返回nullgc时回收必传参数

使用的坑

注意软引用 / 弱引用的一个坑:

String s = "string";
WeakReference<String> ref = new WeakReference<>(s);

// 错误写法
if (ref.get() != null) {
    ref.get().length(); // 此处可能已经被回收,导致空指针
}

// 正确写法 1
String s1 = ref.get(); // 临时变为强引用
if (s1 != null) {
    s1.length();
}

// 正确写法 2
String s2;
if ((s2 = ref.get()) != null) {
    s2.length();
}

应用

SoftReference、WeakReference都可以用作缓存,例如Glide图片库使用了WeakReference缓存。

WeakReference还可以用于监控内存,例如LeakCanary。

PhantomReferences和WeakReference类似,区别是PhantomReferences不能获取到目标对象。

PhantomReferences的两种作用:

  • 监控对象是否已回收,以便实现内存敏感的需求,例如可以等待大对象回收后再创建下一个。
  • 代替finalize方法,自行执行回收操作,减小gc压力。

例:LeakCanary在Android中监控内存泄露

LeakCanary可用于检测Android内存泄露,内存追踪使用了WeakReference。

基于LeakCanary 1.5.4源码,流程描述如下:

  1. ActivityRefWatcher:Activity.onDestroy()时,添加Activity对象到RefWatcher监控列表中(创建一个虚引用)。
  2. AndroidWatchExecutor:延时5s。
  3. RefWatcher:通过ReferenceQueue,判断对象是否已回收。
  4. GcTrigger:如果没有回收,触发gc。
  5. RefWatcher:判断对象是否已回收。
  6. AndroidHeapDumper:如果没有回收,可能有内存泄露。调用Android提供的Debug.dumpHprofData(),生成Heap Dump(堆转储文件)。
  7. HeapAnalyzerService:调用haha模块分析内存泄露。

参考: LeakCanary 源码解析

例:PhantomReferences代替finalize方法执行回收操作

Java对象在被gc回收时,finalize方法会被调用。如果在finalize中执行了复杂的任务,会增加gc压力,因此可以借助PhantomReferences自行处理。

代码来自 Phantom References in Java

public class LargeObjectFinalizer extends PhantomReference<Object> {

    public LargeObjectFinalizer(
      Object referent, ReferenceQueue<? super Object> q) {
        super(referent, q);
    }
     
    public void finalizeResources() {
        // free resources
        System.out.println("clearing ...");
    }
}
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
List<LargeObjectFinalizer> references = new ArrayList<>();
List<Object> largeObjects = new ArrayList<>();

for (int i = 0; i < 10; ++i) {
    Object largeObject = new Object();
    largeObjects.add(largeObject);
    references.add(new LargeObjectFinalizer(largeObject, referenceQueue));
}

largeObjects = null;
System.gc();

Reference<?> referenceFromQueue;
for (PhantomReference<Object> reference : references) {
    System.out.println(reference.isEnqueued());
}

// finalize resources
while ((referenceFromQueue = referenceQueue.poll()) != null) {
    ((LargeObjectFinalizer)referenceFromQueue).finalizeResources();
    referenceFromQueue.clear();
}

最后,欢迎扫码关注微信公众号。微软 / Shopee / Coupang / BAT等国内外企业内推、行业和技术交流,也可以加我微信 jzj2015(注明来自博客),拉你进技术群。

本文由原创,转载请注明来源:https://www.paincker.com/java-reference
(标注了原文链接的文章除外)

0

暂无评论

评论前:需填写以下信息,或 登录

用户登录

忘记密码?