終極理解弱引用在Android和Java中的工作原理[譯]

幾周前我有幸參加了在波蘭舉行的國際移動會議失息,這是移動開發(fā)者最好的會議之一守伸。在“最佳實踐”系列演講中,我的朋友兼同事Jorge Barroso 的一個觀點引起了我的注意:

如果你是一個Android開發(fā)者,但是你沒有使用弱引用,那么你會有麻煩。

剛好,幾個月前我和Diego Grancini合作出版了我的上一本書,“Android High Performance”恨樟。其中最熱門的一章就是討論Android中的內存管理养晋。在這一章中,我們討論了在移動設備上內存是怎么工作的,內存泄漏是怎么發(fā)生的瓣赂,內存泄漏這個問題為什么如此重要以及我們需要采取什么技術來避免穆碎。自從我從事Android開發(fā)以來恭金,我注意到一種傾向揪惦,凡是和內存泄漏或者內存管理有關的事情钩杰,開發(fā)者總是不由自主的回避或者降低其優(yōu)先級媳荒。如果功能需求已經(jīng)滿足蝌借,為什么要自尋煩惱?我們總是急于開發(fā)新的功能勤揩,我們寧愿在我們的下一個Sprint演示中呈現(xiàn)一些視覺效果蛙埂,而不是關心那些人們不會第一眼就發(fā)現(xiàn)的問題燎悍。

一個不錯的觀點就是,這將不可避免的導致技術債務触机。我甚至還補充一點蔬胯,技術債務在現(xiàn)實世界也會產生一些影響迈勋,這些影響我們無法通過單元測試來衡量:失望鼻吮、開發(fā)者之間扯皮蹈垢、軟件交付質量低下以及工作激情消失后添。這個影響很難衡量的原因是它們常常發(fā)生在未來的某一個時間點硫嘶。它的發(fā)生有一點像政客:如果我僅僅當政8年,我為什么要為第12年發(fā)生的事情煩惱忆畅?與之不同的是軟件開發(fā)在飛速發(fā)展绊诲。

如果要寫適用于軟件開發(fā)的思維模式洽胶,需要很長的篇幅,并且已經(jīng)有很多書和文章可供您探索无切,然而簡單的介紹內存引用的不同類型荡短,它們各自的含義以及怎樣把它們應用到Android開發(fā)中則容易的多,這就是我在本文中要做得事情哆键。

首先:Java中什么是引用掘托?

引用指向一個已經(jīng)聲明的對象,你可以訪問它籍嘹。

Java默認有4種引用:強引用闪盔,軟引用弱引用虛引用辱士。也有一些人認為只有兩種引用泪掀,強引用和弱引用,并且弱引用可以表現(xiàn)為兩種形式颂碘。在生活中,我們傾向于將一切東西像植物學家一樣進行分類异赫。不管哪一種分類更適合你,首先你需要理解他們。然后你才能提出你自己的分類方式祝辣。

每一種引用的含義是什么贴妻?

強引用:強引用是Java中最常見的引用切油。每當我們創(chuàng)建一個新的對象蝙斜,默認就創(chuàng)建了一個強引用。比如澎胡,但我們寫下如下代碼:

MyObject object = new MyObject();

當一個MyObject類型的對象被創(chuàng)建孕荠,object就持有一個它的強引用。到目前為止攻谁,還是比較簡單的稚伍,你還能跟上嗎?那么戚宦,接下來會發(fā)生更多有趣的事情个曙。這個Object強可達的-也就是說它可以通過一個強引用鏈到達。這將阻止垃圾回收器回收并且銷毀它受楼,我們最希望的是垃圾回收器及時的回收并銷毀它垦搬。但是現(xiàn)在讓我們一起來看一個例子,這將我和我們想要的不一樣艳汽。

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {   
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        new MyAsyncTask().execute();
    }
    
    private class MyAsyncTask extends AsyncTask {
        @Override
        protected Object doInBackground(Object[] params) {
            return doSomeStuff();
        }
        private Object doSomeStuff() {
            //do something to get result
            return new MyObject();
        } 
    }
}

花幾分鐘時間猴贰,盡可能找出任何容易出現(xiàn)問題的路徑。

不用擔心河狐,如果找不到就多花一點時間米绕。

現(xiàn)在呢?

AsyncTask將在ActivityOnCreate()函數(shù)中創(chuàng)建并執(zhí)行馋艺。但是這里會有一個問題栅干,內部類在他的整個生命周期中都需要訪問外部類。

Activity被銷毀的時候會發(fā)生什么事情捐祠?AsyncTask持有一個Activity的引用非驮,從而導致Activity不能被垃圾回收器回收。這就是所謂的內存泄漏雏赦。

旁記我以前在面試候選人時劫笙,我總是問他們怎樣制造一個內存泄漏,而不是問他們關于內存泄漏的理論知識星岗。通常都很有意思填大。

這里的內存泄漏實際上不僅僅在Activity自身銷毀時發(fā)生,同樣俏橘,由于系統(tǒng)配置發(fā)生變化或者系統(tǒng)需要更多的內存等而被強制銷毀時也會發(fā)生允华。如果這個AsyncTask比較復雜(比如持有Activity里的Views的引用等),這還會引發(fā)程序崩潰,因為view的引用是null靴寂。

那么怎樣才能防止這個問題再次發(fā)生磷蜀? 讓我們解釋另一種類型的引用:

弱引用:弱引用是指不夠強大到讓系統(tǒng)保存在內存中的引用。如果我們嘗試確定一個對象是否被強引用百炬,而剛好是通過WeakRerences方式引用褐隆,那么這該對象將被回收。為了理解剖踊,最好是消化掉理論知識庶弃,我們通過一個實際的樣例來展示怎樣通過使用WeakReference來避免上一個例子中的內存泄漏:

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new MyAsyncTask(this).execute();
    }
    private static class MyAsyncTask extends AsyncTask {
        private WeakReference<MainActivity> mainActivity;    
        
        public MyAsyncTask(MainActivity mainActivity) {   
            this.mainActivity = new WeakReference<>(mainActivity);            
        }
        @Override
        protected Object doInBackground(Object[] params) {
            return doSomeStuff();
        }
        private Object doSomeStuff() {
            //do something to get result
            return new Object();
        }
        @Override
        protected void onPostExecute(Object object) {
            super.onPostExecute(object);
            if (mainActivity.get() != null){
                //adapt contents
            }
        }
    }
}

注意主要的變化:內部類是通過下面的方式引用Activity的:

private WeakReference<MainActivity> mainActivity;

當Activity需要被銷毀時會發(fā)生什么事情?由于它是通過弱引用的方式持有德澈,它可以被回收歇攻。所以不會有內存泄漏發(fā)生。

旁記現(xiàn)在希望你已經(jīng)更好的理解了什么是弱引用梆造,你會發(fā)現(xiàn)一個非常有用的類 WeakHashMap缴守。 它就是一個HashMap,除了它的keys(注意是key镇辉, 不是values)是通過弱引用的方式被引用的屡穗。這使得它對于實現(xiàn)高速緩存之類的實體是非常有用的

我們已經(jīng)提到了更多的引用摊聋。 讓我們看看在什么情況下它們是有用的鸡捐,以及我們如何能夠從中受益:

軟引用:把軟引用看做一個較強的弱引用軟引用會要求垃圾回收將其保留在內存中麻裁,如果沒有其他選項時才將其回收箍镜,而弱引用是被立即回收。垃圾回收算法真的是令人興奮的東西煎源,所以有時你會花數(shù)小時去研究而不知疲倦色迂。但是基本規(guī)則就是:”我總是要回收弱引用。如果一個對象是軟引用手销,我會根據(jù)系統(tǒng)環(huán)境來決定做什么“歇僧。這使得軟引用對于實現(xiàn)緩存非常有用:只要內存是充裕的,我們不用擔心手動移除對象锋拖。如果你想查看一個實際的例子诈悍,你可以查看這個通過軟引用實現(xiàn)的緩存樣例

虛引用:啊哈兽埃,虛引用侥钳!我想我一只手就能數(shù)過來在生產環(huán)境中我所見到的虛引用被使用的情形。一個對象如果僅僅被通過虛引用的方式引用柄错,那么它會被垃圾回收器隨心所欲的回收舷夺。沒有更多的解釋苦酱,沒有“召回”。這使得它難以描述给猾。為什么我們會喜歡使用這樣一個東西疫萤?難道其他的還不夠麻煩?為什么我選擇成為一名程序員敢伸?虛引用可以被用來精準的檢測一個對象是否被從內存中刪除扯饶。我記得我的整個職業(yè)生涯中一共使用了兩次虛引用。所以如果你現(xiàn)在感覺虛引用很難理解详拙,請不要沮喪帝际。

希望本文能夠理清一點點你以前對引用的認識蔓同。在任何學習過程中饶辙,你也許想開始實踐實踐,把弄一下自己的代碼并看看你如何改進它斑粱。首先你需要檢查你的代碼中是否存在內存泄漏的問題弃揽,進而使用你從這些課程中學習到的知識去消除這些令人難受的內存泄漏。如果喜歡本文或者你覺得本文對你有所幫助则北,請隨時分享并/或者留下評論矿微。這是對業(yè)余寫作者最大的鼓勵。

原文鏈接:Finally understanding how references work in Android and Java

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末尚揣,一起剝皮案震驚了整個濱河市涌矢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌快骗,老刑警劉巖娜庇,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異方篮,居然都是意外死亡名秀,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門藕溅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來匕得,“玉大人,你說我怎么就攤上這事巾表≈樱” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵集币,是天一觀的道長考阱。 經(jīng)常有香客問我,道長惠猿,這世上最難降的妖魔是什么羔砾? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任负间,我火速辦了婚禮,結果婚禮上姜凄,老公的妹妹穿的比我還像新娘政溃。我一直安慰自己,他們只是感情好态秧,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布董虱。 她就那樣靜靜地躺著,像睡著了一般申鱼。 火紅的嫁衣襯著肌膚如雪愤诱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天捐友,我揣著相機與錄音淫半,去河邊找鬼。 笑死匣砖,一個胖子當著我的面吹牛科吭,可吹牛的內容都是我干的。 我是一名探鬼主播猴鲫,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼对人,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拂共?” 一聲冷哼從身側響起牺弄,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宜狐,沒想到半個月后势告,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡肌厨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年培慌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柑爸。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡吵护,死狀恐怖,靈堂內的尸體忽然破棺而出表鳍,到底是詐尸還是另有隱情馅而,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布譬圣,位于F島的核電站瓮恭,受9級特大地震影響,放射性物質發(fā)生泄漏厘熟。R本人自食惡果不足惜屯蹦,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一维哈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧登澜,春花似錦阔挠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谴仙,卻和暖如春迂求,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晃跺。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工揩局, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人哼审。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓谐腰,卻偏偏與公主長得像孕豹,于是被迫代替她去往敵國和親涩盾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內容