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.weakreference@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-01-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中应用详解,希望对大家有所帮助
下一篇: PHP实现新型冠状病毒疫情实时图的实例
推荐阅读
-
java四种引用及在LeakCanery中应用详解
-
Java日期时间API系列5-----Jdk7及以前的日期时间类TimeUnit在并发编程中的应用
-
java四种引用及在LeakCanery中应用详解
-
Java日期时间API系列5-----Jdk7及以前的日期时间类TimeUnit在并发编程中的应用
-
实例详解Queue在Java中应用
-
详解设计模式中的proxy代理模式及在Java程序中的实现
-
详解设计模式中的proxy代理模式及在Java程序中的实现
-
SVGA JAVA库在源码AOSP Android.mk中引用及应用
-
java中四种访问修饰符区别及详解全过程
-
实例详解Queue在Java中应用