Android智能指針Summary

引言:由于未來需要深入android底層進行系統級別的開發,所以最近在看老羅的《Android系統源代碼情景分析》,里面提到了一個很重要的概念叫智能指針,這個東西對于理解android應用系統框架很有幫助,在FrameWork層大量的C++代碼中會經常使用到這個概念。


那么什么是智能指針呢?(這里提下,這個指針不是指*,指的是一個對象,但是它引用了一個實際使用的對象)

書的原話是:智能指針式一種能夠自動維護對象引用計數的技術。

具體一點解釋,大家都知道C++需要使用大量的指針,指針最容易出現錯誤的地方就是忘記釋放其指向的對象所占的內存導致內存泄漏。那么為了避免這種情況,Android系統提供了C++智能指針通過引用計數技術來維護對象的生命周期。(下面稍微解釋一下引用計數技術,我相信很多人都了解過)

引用計數法:這種算法的思路是如果某一個對象被別的對象,那么就把他們引用計數器加上1,這樣當進行垃圾回收時如果判斷該引用的數量為0,此時就代表沒有進行任何對象對其進行引用,此時就進行回收。

問題:但是這種技術會出現一個問題就是兩個對象相互引用的時候會出現“死鎖”的情況。比如A引用B,B引用A。當對象A不再使用需要釋放它所占的內存時,由于A仍然被B引用所以無法釋放,只能等待B釋放這個引用,同樣對B來說一樣的問題。所以會造成相互等待,這個和Java中的鎖同步問題一個道理,A對象wait()了自己等待B對象喚醒,B對象也wait()了自己等待A對象喚醒自己。就如兩個睡美人都在等待對方叫醒自己一樣造成死鎖狀態。

在java中為了解決這個問題引入了引用鏈方法,這里僅僅提一下這個概念--“JVM采用GC Roots可達性來決定是否會被GC回收",可以參考《深入JVM虛擬機》一書。

那么Android的智能指針是怎么解決這個問題的呢?

這里先介紹一種較為復雜的引用計數方法,這種方法將對象的引用計數分為強引用和若引用計數兩種,但是對象的生命周期只受強引用計數控制。這種解決方案以”父子“關系將對象很有意思的關聯了起來,即"父”對象通過強引用計數引用”子"對象,“子”對象通過弱引用計數引用“父”對象,但是很明顯按照傳統美德只有父親管著兒子,所以當“子”對象想要釋放自己時由于它還收到“父”對象的管制無法釋放自己;但是“父”對象想要釋放自己時可以輕易釋放自己,此時由于“父”不存在了,“子”對象不受強引用計數的管制了就可以釋放自己了。

好的介紹完了這些背景可以公布答案了,答案就是:Android提供了三種類型的指針,分別為輕量級指針(Light Pointer)、強指針(Strong Pointer)、弱指針(Weak Pointer)。

輕量級指針

這里不多提輕量級指針,因為這種指針式通過簡單引用計數技術來維護對象生命周期的。(個人覺得還是會有相互引用的風險產生,所以并沒有懂使用這個指針的意義在哪兒?也許是相比強指針和弱指針其效率更高吧)。關于它只需知道3點:

第一點使用它需要繼承LightRefBase(模板類)

public LightClass: public LightRefBase<LightClass>

第二點LightRefBase類只有一個成員變量mCount用來描述一個對象的引用計數值。

第三點需要知道輕量級指針的實現類和強指針的實現類是同一個類sp。

強指針

與輕量級指針不同,強指針不是直接使用一個整數來維護對象的引用計數的,而是使用一個weakref_impl對象,這個對象是繼承RefBase類(一個類要使用強指針和弱指針必須繼承RefBase)中的內部類weakref_type類,其中weakref_type僅僅只定義了引用計數維護接口,具體實現是weakref_type。(具體關系如下圖,圖是手碼的,一個是繼承關系,一個是引用關系)

這里說一下成員變量mFlags的作用,mFlags這個標志位有三種取值:

0:表示對象的生命周期只受強引用計數影響;默認就是這個。

1(OBJECT_LIFETIME_WEAK):表示對象的生命周期同時受強引用計數和弱引用計數影響

OBJECT_LIFETIME_FOREVER:表示對象的生命周期完全不受強引用計數和弱引用計數的影響。//這個地方我想說一下,我實踐的時候發現并沒有這個標志位,可能是后來的android版本在基類里面取消了這個標識位,具體我還沒有仔細查最新的源碼,會繼續補充。

*原書里面解釋強指針或弱指針均涉及源代碼分析,這里我嘗試用自己的語言總結一下重要的部分


RefBase的incStrong函數干了哪些事情呢?(主要有三步,第三步是第一次強引用的一些邏輯處理,這里不分析)

1.增加弱引用計數(這個看起來好像與函數的名字有點相互違背,這個后面會解釋)

具體過程:通過mRefs的incWeak方法來增加對象的弱引用計數(可以配合類圖理解),mRefs是Weakref_impI類型的,Weakref_impl又繼承了inWeak方法,實際上調用的是weakref_type的方法

2.增加強引用計數。

通過android_atomic_inc函數增加強引用計數值(返回增加前的值,這里注意是之前)

可以看出強指針類增加對象的強引用計數的同時也會增加弱引用計數,即一個對象的弱引用計數一定是大于或者等于它的強引用計數的。(sp的構造函數就干了這么些事情)

那么sp的析構函數干了什么事情呢?(對應函數decStrong)

1.減少對象的強引用計數,當強引用計數為0時(實際上不是0,這里用0好解釋),即不再被強指針引用時。此時需要判斷標識位mFlags(上面提過)是否為1,如果不為1,就會釋放對象所占的內存,同時也會導致RefBase類的析構函數調用。

2.減少對象的弱引用計數,一旦發現弱引用計數為0時,把引用計數對象mRefs(weakref_impl類型)也釋放掉(前面提過,建議回頭看看方便理解)。前面說過,一個對象的弱引用計數一定大于或者等于強引用計數的,當強引用計數為0時,會釋放掉RefBase對象,但當此時弱引用計數大于0時,不能將mRefs也釋放掉,因為還有其他的弱指針通過weakref_impl對象來引用實際的對象。

*如果還是不懂,建議配合原書中的源代碼看。

弱指針

弱指針同樣從RefBase類繼承下來,因為RefBase提供了弱引用計數器。弱指針類的實現類為wp。弱指針使用的是類型為weakref_type*的成員變量m_refs維護對象的弱引用計數。

弱指針和強指針有一個很大的區別,就是弱指針不可以直接操作它所引用的對象,因為它所引用的對象可能是不受弱引用計數控制的,即它所引用的對象可能是一個無效的對象。因此,如果需要操作一個弱指針所引用的對象,那么就需要將這個弱指針升級為強指針,這是通過它的成員函數promote來實現的。如果升級成功,就說明該弱指針所引用的對象還沒有被銷毀,可以正常使用。

下面著重介紹wp的promote函數。先來看兩段源代碼代碼(純手碼截圖,下次用markdown編輯器寫,這么寫太sb了)

RefBase.h

參數p指向對象的地址,而參數refs指向該對象內部的一個弱引用計數器對象。只有在對象地址不為null的情況下,才會調用它內部的弱引用計數器對象的成員函數attempIncStrong來試圖增加該對象的強引用計數。如果能夠成功增加對象的強引用計數,那么就可以成功地把一個弱指針升級為一個強指針。

attempIncStrong看著是不是很熟悉,可以從之前的圖中找到。

這個成員函數試圖增加目標對象的強引用計數,但是有可能會增加失敗,因為目標對象可能已經被釋放了,或者該目標對象不允許使用強指針引用它。

(attempIncStrong中有個有意思的邏輯)

之前提過增加對象強引用計數時,同時也會增加該對象的弱引用計數。

分割線(邏輯來了)

1.先調用成員函數incWeak來增加對象的弱引用計數 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2.如果后面增加對象的強引用計數失敗,則調用decWeak來減少對象的弱引用計數。

一個弱指針所引用的對象可能處于兩種狀態。(下面均摘自原文)

第一種:該對象同時也被其他強指針對象所引用,此時可以安全地將這個弱指針升級為強指針。

第二種:該對象沒有被任何強指針引用。這里情況就比較復雜了。需要根據對象生命周期來判斷

1.如果對象生命周期只受強引用計數影響,那么就可以成功將該弱指針升級為強指針。因為它受強引用計數影響,而此時該對象又沒有被強指針引用過,那么它必然不會被釋放。

2.如果只受弱引用計數影響,首先我們可以確定對象現在一定是存在的,因為現在有一個弱指針引用它。但是,這種情況需要進一步調用對象的成員函數onIncStrongAttempted來確認對象是否允許強指針引用它。如果返回為true說明允許則成功將該弱指針升級為強指針。如果返回為false,則說明升級失敗。

大概就總結這么多。下面是打賞時間,碼字不易,給個贊也行。


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,076評論 2 375

推薦閱讀更多精彩內容