Java/Android引用類型及其使用全面分析
Java/Android中有四種引用類型,分別是:
Strong reference - 強(qiáng)引用
Soft Reference - 軟引用
Weak Reference - 弱引用
Phantom Reference - 虛引用
不同的引用類型有著不同的特性,同時(shí)也對(duì)應(yīng)著不同的使用場(chǎng)景。
1.Strong reference - 強(qiáng)引用
實(shí)際編碼中最常見的一種引用類型。常見形式如:A a = new A();等。強(qiáng)引用本身存儲(chǔ)在棧內(nèi)存中,其存儲(chǔ)指向?qū)?nèi)存中對(duì)象的地址。一般情況下,當(dāng)對(duì)內(nèi)存中的對(duì)象不再有任何強(qiáng)引用指向它時(shí),垃圾回收機(jī)器開始考慮可能要對(duì)此內(nèi)存進(jìn)行的垃圾回收。如當(dāng)進(jìn)行編碼:a = null,此時(shí),剛剛在堆中分配地址并新建的a對(duì)象沒(méi)有其他的任何引用,當(dāng)系統(tǒng)進(jìn)行垃圾回收時(shí),堆內(nèi)存將被垃圾回收。
SoftReference、WeakReference、PhantomReference都是類java.lang.ref.Reference的子類。Reference作為抽象基類,定義了其子類對(duì)象的基本操作。Reference子類都具有如下特點(diǎn):
1.Reference子類不能無(wú)參化直接創(chuàng)建,必須至少以強(qiáng)引用對(duì)象為構(gòu)造參數(shù),創(chuàng)建各自的子類對(duì)象;
2.因?yàn)?中以強(qiáng)引用對(duì)象為構(gòu)造參數(shù)創(chuàng)建對(duì)象,因此,使得原本強(qiáng)引用所指向的堆內(nèi)存中的對(duì)象將不再只與強(qiáng)引用本身直接關(guān)聯(lián),與Reference的子類對(duì)象的引用也有一定聯(lián)系。且此種聯(lián)系將可能影響到對(duì)象的垃圾回收。
根據(jù)不同的子類對(duì)象對(duì)其指示對(duì)象(強(qiáng)引用所指向的堆內(nèi)存中的對(duì)象)的垃圾回收不同的影響特點(diǎn),分別形成了三個(gè)子類,即SoftReference、WeakReference和PhantomReference。
2.Soft Reference - 軟引用
軟引用的一般使用形式如下:
A a = new A();
SoftReference<A> srA = new SoftReference<A>(a);
通過(guò)對(duì)象的強(qiáng)引用為參數(shù),創(chuàng)建了一個(gè)SoftReference對(duì)象,并使棧內(nèi)存中的wrA指向此對(duì)象。
此時(shí),進(jìn)行如下編碼:a = null,對(duì)于原本a所指向的A對(duì)象的垃圾回收有什么影響呢?
先直接看一下下面一段程序的輸出結(jié)果:
import java.lang.ref.SoftReference; public class ReferenceTest { public static void main(String[] args) { A a = new A(); SoftReference<A> srA = new SoftReference<A>(a); a = null; if (srA.get() == null) { System.out.println("a對(duì)象進(jìn)入垃圾回收流程"); } else { System.out.println("a對(duì)象尚未被回收" + srA.get()); } // 垃圾回收 System.gc(); if (srA.get() == null) { System.out.println("a對(duì)象進(jìn)入垃圾回收流程"); } else { System.out.println("a對(duì)象尚未被回收" + srA.get()); } } } class A { }
##輸出結(jié)果為:
1 a對(duì)象尚未被回收A@4807ccf6 2 a對(duì)象尚未被回收A@4807ccf6
當(dāng) a = null后,堆內(nèi)存中的A對(duì)象將不再有任何的強(qiáng)引用指向它,但此時(shí)尚存在srA引用的對(duì)象指向A對(duì)象。當(dāng)?shù)谝淮握{(diào)用srA.get()方法返回此指示對(duì)象時(shí),由于垃圾回收器很有可能尚未進(jìn)行垃圾回收,此時(shí)get()是有結(jié)果的,這個(gè)很好理解。當(dāng)程序執(zhí)行System.gc();強(qiáng)制垃圾回收后,通過(guò)srA.get(),發(fā)現(xiàn)依然可以得到所指示的A對(duì)象,說(shuō)明A對(duì)象并未被垃圾回收。那么,軟引用所指示的對(duì)象什么時(shí)候才開始被垃圾回收呢?需要滿足如下兩個(gè)條件:
1.當(dāng)其指示的對(duì)象沒(méi)有任何強(qiáng)引用對(duì)象指向它;
2.當(dāng)虛擬機(jī)內(nèi)存不足時(shí)。
因此,SoftReference變相的延長(zhǎng)了其指示對(duì)象占據(jù)堆內(nèi)存的時(shí)間,直到虛擬機(jī)內(nèi)存不足時(shí)垃圾回收器才回收此堆內(nèi)存空間。
3.Weak Reference - 弱引用
同樣的,軟引用的一般使用形式如下:
A a = new A();
WeakReference<A> wrA = new WeakReference<A>(a);
當(dāng)沒(méi)有任何強(qiáng)引用指向此對(duì)象時(shí), 其垃圾回收又具有什么特性呢?
import java.lang.ref.WeakReference; public class ReferenceTest { public static void main(String[] args) { A a = new A(); WeakReference<A> wrA = new WeakReference<A>(a); a = null; if (wrA.get() == null) { System.out.println("a對(duì)象進(jìn)入垃圾回收流程"); } else { System.out.println("a對(duì)象尚未被回收" + wrA.get()); } // 垃圾回收 System.gc(); if (wrA.get() == null) { System.out.println("a對(duì)象進(jìn)入垃圾回收流程"); } else { System.out.println("a對(duì)象尚未被回收" + wrA.get()); } } } class A { }
##輸出結(jié)果為:
a對(duì)象尚未被回收A@52e5376a a對(duì)象進(jìn)入垃圾回收流程
輸出的第一條結(jié)果解釋同上。當(dāng)進(jìn)行垃圾回收后,wrA.get()將返回null,表明其指示對(duì)象進(jìn)入到了垃圾回收過(guò)程中。因此,對(duì)弱引用特點(diǎn)總結(jié)為:
WeakReference不改變?cè)袕?qiáng)引用對(duì)象的垃圾回收時(shí)機(jī),一旦其指示對(duì)象沒(méi)有任何強(qiáng)引用對(duì)象時(shí),此對(duì)象即進(jìn)入正常的垃圾回收流程。
那么,依據(jù)此特點(diǎn),很可能有疑問(wèn):WeakReference存在又有什么意義呢?
其主要使用場(chǎng)景見于:當(dāng)前已有強(qiáng)引用指向強(qiáng)引用對(duì)象,此時(shí)由于業(yè)務(wù)需要,需要增加對(duì)此對(duì)象的引用,同時(shí)又不希望改變此引用的垃圾回收時(shí)機(jī),此時(shí)WeakReference正好符合需求,常見于一些與生命周期的場(chǎng)景中。
下面給出一個(gè)Android中關(guān)于WeakReference使用的場(chǎng)景 —— 結(jié)合靜態(tài)內(nèi)部類和WeakReference來(lái)解決Activity中可能存在的Handler內(nèi)存泄露問(wèn)題。
Activity中我們需要新建一個(gè)線程獲取數(shù)據(jù),使用handler - sendMessage方式。下面是這一過(guò)程的一般性代碼:
public class MainActivity extends Activity { //... private int page; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 1) { //... page++; } else { //... } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //... new Thread(new Runnable() { @Override public void run() { //.. Message msg = Message.obtain(); msg.what = 1; //msg.obj = xx; handler.sendMessage(msg); } }).start(); //... } }
在Eclispe中Run Link,將會(huì)看到警示信息:This Handler class should be static or leaks might occur ...點(diǎn)擊查看此信息,其詳情中對(duì)問(wèn)題進(jìn)行了說(shuō)明并給出了建議性的解決方案。
Issue: Ensures that Handler classes do not hold on to a reference to an outer class Id: HandlerLeak Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class;In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.
大致的意思是建議將Handler定義成內(nèi)部靜態(tài)類,并在此靜態(tài)內(nèi)部類中定義一個(gè)WeakReference的引用,由于指示外部的Activity對(duì)象。
問(wèn)題分析:
Activity具有自身的生命周期,Activity中新開啟的線程運(yùn)行過(guò)程中,可能此時(shí)用戶按下了Back鍵,或系統(tǒng)內(nèi)存不足等希望回收此Activity,由于Activity中新起的線程并不會(huì)遵循Activity本身的什么周期,也就是說(shuō),當(dāng)Activity執(zhí)行了onDestroy,由于線程以及Handler 的HandleMessage的存在,使得系統(tǒng)本希望進(jìn)行此Activity內(nèi)存回收不能實(shí)現(xiàn),因?yàn)榉庆o態(tài)內(nèi)部類中隱性的持有對(duì)外部類的引用,導(dǎo)致可能存在的內(nèi)存泄露問(wèn)題。
因此,在Activity中使用Handler時(shí),一方面需要將其定義為靜態(tài)內(nèi)部類形式,這樣可以使其與外部類(Activity)解耦,不再持有外部類的引用,同時(shí)由于Handler中的handlerMessage一般都會(huì)多少需要訪問(wèn)或修改Activity的屬性,此時(shí),需要在Handler內(nèi)部定義指向此Activity的WeakReference,使其不會(huì)影響到Activity的內(nèi)存回收同時(shí),可以在正常情況下訪問(wèn)到Activity的屬性。
Google官方給出的建議寫法為:
public class MainActivity extends Activity { //... private int page; private MyHandler mMyHandler = new MyHandler(this); private static class MyHandler extends Handler { private WeakReference<MainActivity> wrActivity; public MyHandler(MainActivity activity) { this.wrActivity = new WeakReference<MainActivity>(activity); } @Override public void handleMessage(Message msg) { if (wrActivity.get() == null) { return; } MainActivity mActivity = wrActivity.get(); if (msg.what == 1) { //... mActivity.page++; } else { //... } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //... new Thread(new Runnable() { @Override public void run() { //.. Message msg = Message.obtain(); msg.what = 1; //msg.obj = xx; mMyHandler.sendMessage(msg); } }).start(); //... } }
對(duì)于SoftReference和WeakReference,還有一個(gè)構(gòu)造器參數(shù)為ReferenceQueue<T>,當(dāng)SoftReference或WeakReference所指示的對(duì)象確實(shí)被垃圾回收后,其引用將被放置于ReferenceQueue中。注意上文中,當(dāng)SoftReference或WeakReference的get()方法返回null時(shí),僅是表明其指示的對(duì)象已經(jīng)進(jìn)入垃圾回收流程,此時(shí)對(duì)象不一定已經(jīng)被垃圾回收。而只有確認(rèn)被垃圾回收后,如果ReferenceQueue,其引用才會(huì)被放置于ReferenceQueue中。
看下面的一個(gè)例子:
public class ReferenceTest { public static void main(String[] args) { A a = new A(); WeakReference<A> wrA = new WeakReference<A>(a); a = null; if (wrA.get() == null) { System.out.println("a對(duì)象進(jìn)入垃圾回收流程"); } else { System.out.println("a對(duì)象尚未被回收" + wrA.get()); } // 垃圾回收 System.gc(); if (wrA.get() == null) { System.out.println("a對(duì)象進(jìn)入垃圾回收流程"); } else { System.out.println("a對(duì)象尚未被回收" + wrA.get()); } } } class A { @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("in A finalize"); } }
##輸出結(jié)果為:
1 a對(duì)象尚未被回收A@46993aaa 2 a對(duì)象被回收 3 in A finalize
由此,也驗(yàn)證了上文中的“進(jìn)入垃圾回收流程”的說(shuō)法。下面結(jié)合ReferenceQueue,看一段代碼:
public class ReferenceTest { public static void main(String[] args) { A a = new A(); ReferenceQueue<A> rq = new ReferenceQueue<A>(); WeakReference<A> wrA = new WeakReference<A>(a, rq); a = null; if (wrA.get() == null) { System.out.println("a對(duì)象進(jìn)入垃圾回收流程"); } else { System.out.println("a對(duì)象尚未被回收" + wrA.get()); } System.out.println("rq item:" + rq.poll()); // 垃圾回收 System.gc(); if (wrA.get() == null) { System.out.println("a對(duì)象進(jìn)入垃圾回收流程"); } else { System.out.println("a對(duì)象尚未被回收" + wrA.get()); } /* try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } */ System.out.println("rq item:" + rq.poll()); } } class A { @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("in A finalize"); } }
##輸出結(jié)果為:
a對(duì)象尚未被回收A@302b2c81 rq item:null a對(duì)象進(jìn)入垃圾回收流程 rq item:null in A finalize
由此,驗(yàn)證了“僅進(jìn)入垃圾回收流程的SoftReference或WeakReference引用尚未被加入到ReferenceQueue”。
public class ReferenceTest { public static void main(String[] args) { A a = new A(); ReferenceQueue<A> rq = new ReferenceQueue<A>(); WeakReference<A> wrA = new WeakReference<A>(a, rq); a = null; if (wrA.get() == null) { System.out.println("a對(duì)象進(jìn)入垃圾回收流程"); } else { System.out.println("a對(duì)象尚未被回收" + wrA.get()); } System.out.println("rq item:" + rq.poll()); // 垃圾回收 System.gc(); if (wrA.get() == null) { System.out.println("a對(duì)象進(jìn)入垃圾回收流程"); } else { System.out.println("a對(duì)象尚未被回收" + wrA.get()); } try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("rq item:" + rq.poll()); } } class A { @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("in A finalize"); } }
##輸出結(jié)果為:
a對(duì)象尚未被回收A@6276e1db rq item:null a對(duì)象進(jìn)入垃圾回收流程 in A finalize rq item:java.lang.ref.WeakReference@645064f
由此,證實(shí)了上述說(shuō)法。
4.PhantomReference
與SoftReference或WeakReference相比,PhantomReference主要差別體現(xiàn)在如下幾點(diǎn):
1.PhantomReference只有一個(gè)構(gòu)造函數(shù)PhantomReference(T referent, ReferenceQueue<? super T> q),因此,PhantomReference使用必須結(jié)合ReferenceQueue;
2.不管有無(wú)強(qiáng)引用指向PhantomReference的指示對(duì)象,PhantomReference的get()方法返回結(jié)果都是null。
public class ReferenceTest { public static void main(String[] args) { A a = new A(); ReferenceQueue<A> rq = new ReferenceQueue<A>(); PhantomReference<A> prA = new PhantomReference<A>(a, rq); System.out.println("prA.get():" + prA.get()); a = null; System.gc(); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("rq item:" + rq.poll()); } } class A { }
##輸出結(jié)果為:
prA.get():null rq item:java.lang.ref.PhantomReference@1da12fc0
代碼中的Thread.sleep(1);作用與上例中相同,都是確保垃圾回收線程能夠執(zhí)行。否則,進(jìn)進(jìn)入垃圾回收流程而沒(méi)有真正被垃圾回收的指示對(duì)象的虛引用是不會(huì)被加入到PhantomReference中的。
與WeakReference相同,PhantomReference并不會(huì)改變其指示對(duì)象的垃圾回收時(shí)機(jī)。且可以總結(jié)出:ReferenceQueue的作用主要是用于監(jiān)聽SoftReference/WeakReference/PhantomReference的指示對(duì)象是否已經(jīng)被垃圾回收。
以上就是小編為大家?guī)?lái)的Java/Android引用類型及其使用全面分析的全部?jī)?nèi)容了,希望對(duì)大家有所幫助,多多支持腳本之家~
相關(guān)文章
淺談Java由于不當(dāng)?shù)膱?zhí)行順序?qū)е碌乃梨i
為了保證線程的安全,我們引入了加鎖機(jī)制,但是如果不加限制的使用加鎖,就有可能會(huì)導(dǎo)致順序死鎖(Lock-Ordering Deadlock)。本文將會(huì)討論一下順序死鎖的問(wèn)題。2021-06-06Springboot整合quartz產(chǎn)生錯(cuò)誤及解決方案
這篇文章主要介紹了Springboot整合quartz產(chǎn)生錯(cuò)誤及解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06Mybatis通過(guò)Spring完成代理類注入的流程分析
這篇文章主要介紹了Mybatis通過(guò)Spring完成代理類注入的流程分析,本文通過(guò)實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08mybatis-plus 攔截器敏感字段加解密的實(shí)現(xiàn)
數(shù)據(jù)庫(kù)在保存數(shù)據(jù)時(shí),對(duì)于某些敏感數(shù)據(jù)需要脫敏或者加密處理,本文主要介紹了mybatis-plus 攔截器敏感字段加解密的實(shí)現(xiàn),感興趣的可以了解一下2021-11-11Java/Web調(diào)用Hadoop進(jìn)行MapReduce示例代碼
本篇文章主要介紹了Java/Web調(diào)用Hadoop進(jìn)行MapReduce示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11springboot利用AOP完成日志統(tǒng)計(jì)的詳細(xì)步驟
項(xiàng)目用到了過(guò)濾器,可能有的人會(huì)不理解,之所以用過(guò)濾器是因?yàn)橄胍谌罩居涗沺ost請(qǐng)求的json數(shù)據(jù)。本文重點(diǎn)給大家介紹springboot利用AOP完成日志統(tǒng)計(jì)的詳細(xì)步驟,感興趣的朋友跟隨小編一起看看吧2021-12-12java中SynchronizedList和Vector的區(qū)別詳解
這篇文章主要介紹了java中SynchronizedList和Vector的區(qū)別詳解,Vector是java.util包中的一個(gè)類。 SynchronizedList是java.util.Collections中的一個(gè)靜態(tài)內(nèi)部類。,需要的朋友可以參考下2019-06-06