Android面試一天一題(Day 29:內存泥潭(下))

上一節(jié)有介紹了一些和內存相關的基礎知識滚婉,這一節(jié)就講一下怎么發(fā)現(xiàn)和處理內存問題长豁。對于我們來說进苍,最容易發(fā)現(xiàn)的內存問題當然是OOM(OutOfMemoryError)癣蟋,應用直接Crash痢掠,日志也會很清晰的標明哪個對象OOM了谢肾。這個解決起來也不難剥悟,常見的Bitmap OOM相信大家也知道怎么處理黔漂。

相對于OOM較麻煩一點的就是內存泄露(Memory Leak)车海,每次就露那么一點笛园,就像溫水煮青蛙一樣,很難發(fā)現(xiàn)有變化侍芝。內存泄漏也是造成應用程序OOM的主要原因之一研铆!我們知道Android系統(tǒng)為每個應用程序分配的內存有限,而當一個應用中產(chǎn)生的內存泄漏比較多時州叠,之后我們再申請新的內存時會及其容易產(chǎn)生OOM棵红。

內存泄漏指的是進程中某些對象(垃圾對象)已經(jīng)沒有使用價值了,但是它們卻可以直接或間接地引用到GC roots導致無法被GC回收咧栗。無用的對象占據(jù)著內存空間逆甜,使得實際可使用內存變小虱肄,形象地說法就是內存泄漏了。

面試題:如何檢測內存泄露交煞,如何進行內存優(yōu)化咏窿?

Android系統(tǒng)為每一個應用程序都設置了一個硬性的Dalvik Heap Size最大限制閾值,這個閾值在不同的設備上會因為RAM大小不同而各有差異素征。如果你的應用占用內存空間已經(jīng)接近這個閾值集嵌,此時再嘗試分配內存的話,很容易引起OOM御毅。

較簡單的查看一個應用的內存使用情況可以通過DDMS的Heap視圖查看:


在Android Studio上也可以通過Memory Monitor查看內存中Dalvik Heap的實時變化:



注意:GC過于頻繁容易出現(xiàn)內存抖動根欧,這也是造成應用卡頓的常見原因。

也可以通過命行的方式查看:

adb shell dumpsys meminfo <package_name|pid> [-d]

具體的數(shù)值意義可以查看官網(wǎng)的說明:https://developer.android.com/studio/profile/investigate-ram.html

MAT內存分析工具

詳細的內存使用情況端蛆,可以通過Android Studio的Android Monitor界面凤粗,在Memory那欄有上幾個小圖標,點擊有一個向下箭頭的圖標會自動生成并打開的HPROF視圖今豆。

不過用他來分析內存泄露還不是很智能嫌拣,我們可以借助第三方工具,常見的工具就是MAT了(Memory Analyzer Tool)呆躲,下載地址 http://eclipse.org/mat/downloads.php亭罪,這里我們需要下載獨立版的MAT(之前在使用Ecelipse開發(fā)Android應用時,我們常常會使用它的插件版本)歼秽。

注意:Android Monitor生成的HPROF文件為Dalvik虛擬機格式的,需要轉成J2SE虛擬機格式的情组,否則MAT工具中無法打開燥筷。轉換的方式也很簡單,Android Studio自帶了院崇,直接在“Captures”->"Heap Snapshot"選中剛剛生成的".hprof"文件肆氓,然后鼠標右鍵選擇“Export to standard .hprof”可以在MAT上使用了。

MAT的具體使用方式底瓣,網(wǎng)上很多谢揪,大家可以自己搜一下。這里就提一下用它怎么能快速查找到內存泄露的點捐凭,比如通過“Dominator Tree”的"Path To GC Roots"的排除虛引用/弱引用/軟引用等的引用鏈拨扶,因為被虛引用/弱引用/軟引用的對象可以直接被GC給回收,我們要看的就是某個我們已經(jīng)不需要使用的對象否還存在強引用鏈茁肠。比如患民,我們已退出一個Activity(onDestroy方法也被執(zhí)行了),但在Path To GC Roots中卻發(fā)現(xiàn)這個Activity對象還被有一個引用鏈垦梆,那么就可以確認這個Activity對像就產(chǎn)生了內存泄漏匹颤。一般來說仅孩,從它的引用鏈上也可以直觀地看出是誰在引用它。

除了上面介紹了MAT檢測內存泄露, 有一個叫LeakCanary工具大家也可以嘗試一下印蓖。項目地址:https://github.com/square/leakcanaryLeakCanary會檢測應用的內存回收情況辽慕,如果發(fā)現(xiàn)有垃圾對象沒有被回收,就會去分析當前的內存快照赦肃,也就是上邊MAT用到的.hprof文件溅蛉,找到對象的引用鏈,并顯示在頁面上摆尝。這款插件的好處就是温艇,可以在手機端直接查看內存泄露的地方,可以輔助我們檢測內存泄露堕汞。

開發(fā)中如何避免內存泄漏

這點我比較喜歡問面試者勺爱,希望面試者能羅列出一些他自己遇到過的情況。通常來說讯检,Activity的泄漏是內存泄漏里面最嚴重的問題琐鲁,它占用的內存多(它里面有N多資源的引用),影響比較明顯人灼。下面就示例兩種錯誤的引用方式围段。

錯誤的單例模式

public class Singleton {
    private static Singleton instance;
    private Context mContext;

    private Singleton(Context context) {
        this.mContext = context;
    }

    public static Singleton getInstance(Context context) {
        if (instance == null) {
            instance = new Singleton(context);
        }
        return instance;
    }
}

這是一個非線程安全的單例模式,instance作為靜態(tài)對象投放,其生命周期要長于普通的對象奈泪,其中也包含Activity,假如Activity A去getInstance獲得instance對象灸芳,傳入this涝桅,常駐內存的Singleton保存了你傳入的Activity A對象,并一直持有烙样,即使Activity被銷毀掉冯遂,但因為它的引用還存在于一個Singleton中,就不可能被GC掉谒获,這樣就導致了內存泄漏蛤肌。

View持有Activity引用

public class MainActivity extends Activity {
    private static Drawable mDrawable;

    @Override
    protected void onCreate(Bundle saveInstanceState) {
        super.onCreate(saveInstanceState);
        setContentView(R.layout.activity_main);
        ImageView iv = new ImageView(this);
        mDrawable = getResources().getDrawable(R.drawable.ic_launcher);
        iv.setImageDrawable(mDrawable);
    }
}

有一個靜態(tài)的Drawable對象當ImageView設置這個Drawable時,ImageView保存了mDrawable的引用批狱,而ImageView傳入的this是MainActivity的mContext裸准,因為被static修飾的mDrawable是常駐內存的,MainActivity是它的間接引用赔硫,MainActivity被銷毀時狼速,也不能被GC掉,所以造成內存泄漏。

其實避免Activity的泄漏的方式可以總結為:不要讓生命周期長于Activity的對象持有到Activity的引用向胡。

在開發(fā)中恼蓬,我們也可以給一些初級的工程師相關的建議,如:

  1. 注意單例模式和靜態(tài)變量是否會持有對Context的引用僵芹;
  1. 注意監(jiān)聽器的注銷处硬;(在Android程序里面存在很多需要register與unregister的監(jiān)聽器,我們需要確保在合適的時候及時unregister那些監(jiān)聽器拇派。)
  2. 不要在Thread或AsyncTask中的引用Activity荷辕;

小結

內存泄漏檢測并不屬于一個經(jīng)常會做的事情,所以上面寫的一些東西難免會有一些錯誤件豌。不過我認為在面試中疮方,更關注的是面試者做何去發(fā)現(xiàn)和解決這個問題,然后是否會對遇到過的問題有一個總結茧彤,至于細節(jié)上的東西在真正做的時候會直接得到工具或LOG的反饋骡显,不一定非常記得很清楚的人才說明他會這個東西。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末曾掂,一起剝皮案震驚了整個濱河市惫谤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌珠洗,老刑警劉巖溜歪,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異许蓖,居然都是意外死亡蝴猪,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門膊爪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拯腮,“玉大人,你說我怎么就攤上這事蚁飒。” “怎么了萝喘?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵淮逻,是天一觀的道長。 經(jīng)常有香客問我阁簸,道長爬早,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任启妹,我火速辦了婚禮筛严,結果婚禮上,老公的妹妹穿的比我還像新娘饶米。我一直安慰自己桨啃,他們只是感情好车胡,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著照瘾,像睡著了一般匈棘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上析命,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天主卫,我揣著相機與錄音,去河邊找鬼鹃愤。 笑死簇搅,一個胖子當著我的面吹牛,可吹牛的內容都是我干的软吐。 我是一名探鬼主播瘩将,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼关噪!你這毒婦竟也來了鸟蟹?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤使兔,失蹤者是張志新(化名)和其女友劉穎建钥,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虐沥,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡熊经,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了欲险。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镐依。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖天试,靈堂內的尸體忽然破棺而出槐壳,到底是詐尸還是另有隱情,我是刑警寧澤喜每,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布务唐,位于F島的核電站,受9級特大地震影響带兜,放射性物質發(fā)生泄漏枫笛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一刚照、第九天 我趴在偏房一處隱蔽的房頂上張望刑巧。 院中可真熱鬧,春花似錦、人聲如沸啊楚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽特幔。三九已至咨演,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蚯斯,已是汗流浹背薄风。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拍嵌,地道東北人遭赂。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像横辆,于是被迫代替她去往敵國和親撇他。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容