java四种引用及在LeakCanery中应用详解

网友投稿 232 2022-12-29


java四种引用及在LeakCanery中应用详解

java 四种引用

Java4种引用的级别由高到低依次为:

StrongReference  >  SoftReference  >  WeakReference  >  PhantomReference

1. StrongReference

String tag = new String("T");

此处的 tag 引用就称之为强引用。而强引用有以下特征:

1. 强引用可以直接访问目标对象。

2. 强引用所指向的对象在任何时候都不会被系统回收。

3. 强引用可能导致内存泄漏。

我们要讨论的其它三种Reference较之于强引用而言都属于“弱引用”,也就是他们所引用的对象只要没有强引用,就会根据条件被JVM的垃圾回收器所回收,它们被回收的时机以及用法各不相同。下面分别来进行讨论。

2. SoftReference

软引用有以下特征:

1. 软引用使用 get() 方法取得对象的强引用从而访问目标对象。

2. 软引用所指向的对象按照 JVM 的使用情况(Heap 内存是否临近阈值)来决定是否回收。

3. 软引用可以避免 Heap 内存不足所导致的异常。

当垃圾回收器决定对其回收时,会先清空它的 SoftReference,也就是说 SoftReference 的 get() 方法将会返回 null,然后再调用对象的 finalize() 方法,并在下一轮 GC 中对其真正进行回收。

3. WeakReference

WeakReference 是弱于 SoftReference 的引用类型。弱引用的特性和基本与软引用相似,区别就在于弱引用所指向的对象只要进行系统垃圾回收,不管内存使用情况如何,永远对其进行回收(get() 方法返回 null)。

弱引用有以下特征:

1. 弱引用使用 get() 方法取得对象的强引用从而访问目标对象。

2. 一旦系统内存回收,无论内存是否紧张,弱引用指向的对象都会被回收。

3. 弱引用也可以避免 Heap 内存不足所导致的异常。

4. PhantomReference(虚引用)

PhantomReference 是所有“弱引用”中最弱的引用类型。不同于软引用和弱引用,虚引用无法通过get()方法来取得目标对象的强引用从而使用目标对象,观察源码可以发现 get() 被重写为永远返回 null。

虚引用有以下特征:

虚引用永远无法使用 get() 方法取得对象的强引用从而访问目标对象。

虚引用所指向的对象在被系统内存回收前,虚引用自身会被放入 ReferenceQueue 对象中从而跟踪对象垃圾回收。

虚引用不会根据内存情况自动回收目标对象。

虚引用必须和引用队列(ReferenceQueue)联合使用

Reference与ReferenceQueue 使用demo

定义一个对象Brain

public class Brain {

public int mIndex;

// 占用较多内存,当系统内存不足时,会自动进行回收

private byte []mem;

public Brain(int index) {

mIndex = index;

mem = new byte[1024 * 1024];

}

@Override

protected void finalize() throws Throwable {

super.finalize();

LogUtils.e("Brain", "finalize + index=" + mIndex);

}

}

创建Reference并添加到RefrenceQueue中

结果打印:

2019-01-29 19:22:27.499 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=0 wf=java.lang.ref.WeakReference@e1f904c

2019-01-29 19:22:27.499 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=1 wf=java.lang.ref.WeakReference@82fc895

2019-01-29 19:22:27.500 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=2 wf=java.lang.ref.WeakRVcAyXvqlbkeference@3b3fdaa

2019-01-29 19:22:27.500 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=3 wf=java.lang.ref.WeakReference@668fd9b

2019-01-29 19:22:27.501 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=0

2019-01-29 19:22:27.501 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=100000

2019-01-29 19:22:27.502 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=4 wf=java.lang.ref.WeakReference@8db6538

2019-01-29 19:22:27.502 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=5 wf=java.lang.ref.WeakReference@f915911

2019-01-29 19:22:27.503 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=1

2019-01-29 19:22:27.503 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=100001

2019-01-29 19:22:27.504 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=2

2019-01-29 19:22:27.505 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=100002

2019-01-29 19:22:27.507 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=3

2019-01-29 19:22:27.507 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=100003

2019-01-29 19:22:27.507 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=4

2019-01-29 19:22:27.508 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=100004

2019-01-29 19:22:27.508 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=5

2019-01-29 19:22:27.509 9283-9292/com.selftest.test.testapp3 E/IFLY_SDK_Brain: finalize + index=100005

2019-0http://1-29 19:22:27.629 9283-9309/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了PhantomReference: cnt=0   pr=null

2019-01-29 19:22:27.629 9283-9309/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了PhantomReference: cnt=1   pr=null

2019-01-29 19:22:27.629 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=6 wf=java.lang.ref.WeakReference@e2c4a76

2019-01-29 19:22:27.630 9283-9309/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了PhantomReference: cnt=2   pr=null

2019-01-29 19:22:27.630 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=7 wf=java.lang.ref.WeakReference@4cfd877

2019-01-29 19:22:27.630 9283-9309/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了PhantomReference: cnt=3   pr=null

2019-01-29 19:22:27.630 9283-9309/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了PhantomReference: cnt=4   pr=null

2019-01-29 19:22:27.630 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=8 wf=java.lang.ref.WeakReference@37d9ce4

2019-01-29 19:22:27.630 9283-9309/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了PhantomReference: cnt=5   pr=null

2019-01-29 19:22:27.630 9283-9308/com.selftest.test.testapp3 E/IFLY_SDK_MainActivity: 回收了WeakReference: cnt=9 wf=java.lang.ref.WeakReference@ea1754d

结果分析:

当对象被回收后,持有他的引用WeakReference/PhantomReference会被放入ReferenceQueue中

WeakReference在原始对象回收之前被放入ReferenceQueue中,而PhantomReference是在回收之后放入ReferenceQueue中

WeakReference在Leakcanery中的应用

LeakCanery是android检测内存泄漏的工具,可以检测到Activity/Fragment存在的内存泄漏。

检测原理:

在Application中注册监听所有Activity生命周期的listener,registerActivityLifecycleCallbacks。

//ActivityRefWatcher 中的代码

public void watchActivities() {

// Make sure you don't get installed twice.

stopWatchingActivities();

application.registerActivityLifecycleCallbacks(lifecycleCallbacks);

}

public void stopWatchingActivities() {

application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);

}

当Activity的onDestroy被调用时,生成一个uuid,标记这个Activity的WeakReference。

创建一个弱引用,并与一个跟踪所有activit回收的ReferenceQueue相关联。(放入一个map中,key : uuid, value:weakReference)

private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =

new ActivityLifecycleCallbacksAdapter() {

@Override public void onActivityDestroyed(Activity activity) {

refWatcher.watch(activity);

}

};

具体的watch执行如下:

public void watch(Object watchedReference, String referenceName) {

if (this == DISABLED) {

return;

}

checkNotNull(watchedReference, "watchedReference");

checkNotNull(referenceName, "referenceName");

final long watchStartNanoTime = System.nanoTime();

String key = UUID.randomUUID().toString();

retainedKeys.add(key);

final KeyedWeakReference reference =

new KeyedWeakReference(watchedReference, key, referenceName, queue);

ensureGoneAsync(watchStartNanoTime, reference);

}

ensureGoneAsync执行如下:

// watchExecutor 在一定时间后检查被注册的WeakReference有没有被添加到ReferenceQueue中

private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {

watchExecutor.execute(new Retryable() {

@Override public Retryable.Result run() {

return ensureGone(reference, watchStartNanoTime);

}

});

}

在onDestry被调用后若干秒执行如下操作:到ReferenceQueue中去取这个Activity,如果能够取到说明这个Activity被正常回收了。如果无法回收,触发GC,再去RerenceQueue中取如果还是无法取到,说明Activity没有被系统回收,可能存在内存泄漏。

真正核心的代码如下:

long gcStartNanoTime = System.nanoTime();

long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);

// 如果ReferenceQue中有activity的弱引用,则将retainedKeys中的uuid移除

removeWeaklyReachableReferences();

if (debuggerControl.isDebuggerAttached()) {

// The debugger can create false leaks.

return RETRY;

}

// 如果activity对应的uuid已经被移除,说明activity已经被回收,无内存泄漏

if (gone(reference)) {

return DONE;

}

// 触发gc,进行垃圾回收

gcTrigger.runGc();

removeWeaklyReachableReferences();

// 如果uuid还没有被移除,说明activiy存在内存泄漏,需要dump内存,进行分析

if (!gone(reference)) {

long startDumpHeap = System.nanoTime();

long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);

File heapDumpFile = heapDumper.dumpHeap();

if (heapDumpFile == RETRY_LATER) {

// Could not dump the heap.

return RETRY;

}

long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);

HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)

.referenceName(reference.name)

.watchDurationMs(watchDurationMs)

.gcDurationMs(gcDurationMs)

.heapDumpDurationMs(heapDumpDurationMs)

.build();

heapdumpListener.analyze(heapDump);

}

return DONE;

}

HeapDump dump内存和分析的过程这里就不细说。

总结

以上所述是给大家介绍的java四种引用及在LeakCanery中应用详解,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,会及时回复大家的!


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java8如何将Array转换为Stream的实现
下一篇:手机接口测试工具(手机接口测试APP)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~