java四种引用及在LeakCanery中应用详解
java四种引用
Java4种引用的级别由高到低依次为:
StrongReference > SoftReference > WeakReference > PhantomReference
1.StrongReference
Stringtag=newString("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
publicclassBrain{ publicintmIndex; //占用较多内存,当系统内存不足时,会自动进行回收 privatebyte[]mem; publicBrain(intindex){ mIndex=index; mem=newbyte[1024*1024]; } @Override protectedvoidfinalize()throwsThrowable{ super.finalize(); LogUtils.e("Brain","finalize+index="+mIndex); } }
创建Reference并添加到RefrenceQueue中
结果打印:
2019-01-2919:22:27.4999283-9308/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了WeakReference:cnt=0wf=java.lang.ref.WeakReference@e1f904c
2019-01-2919:22:27.4999283-9308/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了WeakReference:cnt=1wf=java.lang.ref.WeakReference@82fc895
2019-01-2919:22:27.5009283-9308/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了WeakReference:cnt=2wf=java.lang.ref.WeakReference@3b3fdaa
2019-01-2919:22:27.5009283-9308/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了WeakReference:cnt=3wf=java.lang.ref.WeakReference@668fd9b
2019-01-2919:22:27.5019283-9292/com.selftest.test.testapp3E/IFLY_SDK_Brain:finalize+index=0
2019-01-2919:22:27.5019283-9292/com.selftest.test.testapp3E/IFLY_SDK_Brain:finalize+index=100000
2019-01-2919:22:27.5029283-9308/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了WeakReference:cnt=4wf=java.lang.ref.WeakReference@8db6538
2019-01-2919:22:27.5029283-9308/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了WeakReference:cnt=5wf=java.lang.ref.WeakReference@f915911
2019-01-2919:22:27.5039283-9292/com.selftest.test.testapp3E/IFLY_SDK_Brain:finalize+index=1
2019-01-2919:22:27.5039283-9292/com.selftest.test.testapp3E/IFLY_SDK_Brain:finalize+index=100001
2019-01-2919:22:27.5049283-9292/com.selftest.test.testapp3E/IFLY_SDK_Brain:finalize+index=2
2019-01-2919:22:27.5059283-9292/com.selftest.test.testapp3E/IFLY_SDK_Brain:finalize+index=100002
2019-01-2919:22:27.5079283-9292/com.selftest.test.testapp3E/IFLY_SDK_Brain:finalize+index=3
2019-01-2919:22:27.5079283-9292/com.selftest.test.testapp3E/IFLY_SDK_Brain:finalize+index=100003
2019-01-2919:22:27.5079283-9292/com.selftest.test.testapp3E/IFLY_SDK_Brain:finalize+index=4
2019-01-2919:22:27.5089283-9292/com.selftest.test.testapp3E/IFLY_SDK_Brain:finalize+index=100004
2019-01-2919:22:27.5089283-9292/com.selftest.test.testapp3E/IFLY_SDK_Brain:finalize+index=5
2019-01-2919:22:27.5099283-9292/com.selftest.test.testapp3E/IFLY_SDK_Brain:finalize+index=100005
2019-01-2919:22:27.6299283-9309/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了PhantomReference:cnt=0 pr=null
2019-01-2919:22:27.6299283-9309/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了PhantomReference:cnt=1 pr=null
2019-01-2919:22:27.6299283-9308/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了WeakReference:cnt=6wf=java.lang.ref.WeakReference@e2c4a76
2019-01-2919:22:27.6309283-9309/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了PhantomReference:cnt=2 pr=null
2019-01-2919:22:27.6309283-9308/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了WeakReference:cnt=7wf=java.lang.ref.WeakReference@4cfd877
2019-01-2919:22:27.6309283-9309/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了PhantomReference:cnt=3 pr=null
2019-01-2919:22:27.6309283-9309/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了PhantomReference:cnt=4 pr=null
2019-01-2919:22:27.6309283-9308/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了WeakReference:cnt=8wf=java.lang.ref.WeakReference@37d9ce4
2019-01-2919:22:27.6309283-9309/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了PhantomReference:cnt=5 pr=null
2019-01-2919:22:27.6309283-9308/com.selftest.test.testapp3E/IFLY_SDK_MainActivity:回收了WeakReference:cnt=9wf=java.lang.ref.WeakReference@ea1754d
结果分析:
- 当对象被回收后,持有他的引用WeakReference/PhantomReference会被放入ReferenceQueue中
- WeakReference在原始对象回收之前被放入ReferenceQueue中,而PhantomReference是在回收之后放入ReferenceQueue中
WeakReference在Leakcanery中的应用
LeakCanery是Android检测内存泄漏的工具,可以检测到Activity/Fragment存在的内存泄漏。
检测原理:
在Application中注册监听所有Activity生命周期的listener,registerActivityLifecycleCallbacks。
//ActivityRefWatcher中的代码 publicvoidwatchActivities(){ //Makesureyoudon'tgetinstalledtwice. stopWatchingActivities(); application.registerActivityLifecycleCallbacks(lifecycleCallbacks); } publicvoidstopWatchingActivities(){ application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks); }
当Activity的onDestroy被调用时,生成一个uuid,标记这个Activity的WeakReference。
创建一个弱引用,并与一个跟踪所有activit回收的ReferenceQueue相关联。(放入一个map中,key:uuid,value:weakReference)
privatefinalApplication.ActivityLifecycleCallbackslifecycleCallbacks= newActivityLifecycleCallbacksAdapter(){ @OverridepublicvoidonActivityDestroyed(Activityactivity){ refWatcher.watch(activity); } };
具体的watch执行如下:
publicvoidwatch(ObjectwatchedReference,StringreferenceName){ if(this==DISABLED){ return; } checkNotNull(watchedReference,"watchedReference"); checkNotNull(referenceName,"referenceName"); finallongwatchStartNanoTime=System.nanoTime(); Stringkey=UUID.randomUUID().toString(); retainedKeys.add(key); finalKeyedWeakReferencereference= newKeyedWeakReference(watchedReference,key,referenceName,queue); ensureGoneAsync(watchStartNanoTime,reference); }
ensureGoneAsync执行如下:
//watchExecutor在一定时间后检查被注册的WeakReference有没有被添加到ReferenceQueue中 privatevoidensureGoneAsync(finallongwatchStartNanoTime,finalKeyedWeakReferencereference){ watchExecutor.execute(newRetryable(){ @OverridepublicRetryable.Resultrun(){ returnensureGone(reference,watchStartNanoTime); } }); }
在onDestry被调用后若干秒执行如下操作:到ReferenceQueue中去取这个Activity,如果能够取到说明这个Activity被正常回收了。如果无法回收,触发GC,再去RerenceQueue中取如果还是无法取到,说明Activity没有被系统回收,可能存在内存泄漏。
真正核心的代码如下:
longgcStartNanoTime=System.nanoTime(); longwatchDurationMs=NANOSECONDS.toMillis(gcStartNanoTime-watchStartNanoTime); //如果ReferenceQue中有activity的弱引用,则将retainedKeys中的uuid移除 removeWeaklyReachableReferences(); if(debuggerControl.isDebuggerAttached()){ //Thedebuggercancreatefalseleaks. returnRETRY; } //如果activity对应的uuid已经被移除,说明activity已经被回收,无内存泄漏 if(gone(reference)){ returnDONE; } //触发gc,进行垃圾回收 gcTrigger.runGc(); removeWeaklyReachableReferences(); //如果uuid还没有被移除,说明activiy存在内存泄漏,需要dump内存,进行分析 if(!gone(reference)){ longstartDumpHeap=System.nanoTime(); longgcDurationMs=NANOSECONDS.toMillis(startDumpHeap-gcStartNanoTime); FileheapDumpFile=heapDumper.dumpHeap(); if(heapDumpFile==RETRY_LATER){ //Couldnotdumptheheap. returnRETRY; } longheapDumpDurationMs=NANOSECONDS.toMillis(System.nanoTime()-startDumpHeap); HeapDumpheapDump=heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key) .referenceName(reference.name) .watchDurationMs(watchDurationMs) .gcDurationMs(gcDurationMs) .heapDumpDurationMs(heapDumpDurationMs) .build(); heapdumpListener.analyze(heapDump); } returnDONE; }
HeapDumpdump内存和分析的过程这里就不细说。
总结
以上所述是小编给大家介绍的java四种引用及在LeakCanery中应用详解,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!