Android內(nèi)存泄露——全解析和處理辦法

前言

本文章會一步一步的探討內(nèi)存泄露的問題。
博主第一次書寫長篇技術(shù)貼,如有錯誤或不周到的地方請多指教潜慎。

JAVA是垃圾回收語言的一種爪幻,開發(fā)者無需特意管理內(nèi)存分配。但是JAVA中還是存在著許多內(nèi)存泄露的可能性肠套,如果不好好處理內(nèi)存泄露,會導(dǎo)致APP內(nèi)存單元無法釋放被浪費掉,最終導(dǎo)致內(nèi)存全部占據(jù)堆棧(heap)擠爆進(jìn)而程序崩潰憔辫。

內(nèi)存泄露

說到內(nèi)存泄露,就不得不提到內(nèi)存溢出仿荆,這兩個比較容易混淆的概念贰您,我們來分析一下。

  • 內(nèi)存泄露程序在向系統(tǒng)申請分配內(nèi)存空間后(new)拢操,在使用完畢后未釋放锦亦。結(jié)果導(dǎo)致一直占據(jù)該內(nèi)存單元,我們和程序都無法再使用該內(nèi)存單元令境,直到程序結(jié)束杠园,這是內(nèi)存泄露。

  • 內(nèi)存溢出程序向系統(tǒng)申請的內(nèi)存空間超出了系統(tǒng)能給的舔庶。比如內(nèi)存只能分配一個int類型抛蚁,我卻要塞給他一個long類型玲昧,系統(tǒng)就出現(xiàn)oom。又比如一車最多能坐5個人篮绿,你卻非要塞下10個孵延,車就擠爆了。

大量的內(nèi)存泄露會導(dǎo)致內(nèi)存溢出(oom)亲配。

內(nèi)存

想要了解內(nèi)存泄露尘应,對內(nèi)存的了解必不可少。
JAVA是在JVM所虛擬出的內(nèi)存環(huán)境中運行的吼虎,JVM的內(nèi)存可分為三個區(qū):堆(heap)犬钢、棧(stack)和方法區(qū)(method)。

  • 棧(stack):是簡單的數(shù)據(jù)結(jié)構(gòu)思灰,但在計算機(jī)中使用廣泛玷犹。棧最顯著的特征是:LIFO(Last In, First Out, 后進(jìn)先出)。比如我們往箱子里面放衣服洒疚,先放入的在最下方歹颓,只有拿出后來放入的才能拿到下方的衣服。棧中只存放基本類型和對象的引用(不是對象)油湖。

  • 堆(heap)堆內(nèi)存用于存放由new創(chuàng)建的對象和數(shù)組巍扛。在堆中分配的內(nèi)存,由java虛擬機(jī)自動垃圾回收器來管理乏德。JVM只有一個堆區(qū)(heap)被所有線程共享撤奸,堆中不存放基本類型和對象引用,只存放對象本身喊括。

  • 方法區(qū)(method):又叫靜態(tài)區(qū)胧瓜,跟堆一樣,被所有的線程共享郑什。方法區(qū)包含所有的class和static變量府喳。

內(nèi)存的概念大概理解清楚后,要考慮的問題來了:
到底是哪里的內(nèi)存會讓我們造成內(nèi)存泄露蹦误?

好了黑人兄弟既然提問了劫拢,那我們繼續(xù)來嘮!

內(nèi)存泄露原因分析

在JAVA中JVM的棧記錄了方法的調(diào)用强胰,每個線程擁有一個棧。在線程的運行過程當(dāng)中妹沙,執(zhí)行到一個新的方法調(diào)用偶洋,就在棧中增加一個內(nèi)存單元,即幀(frame)距糖。在frame中玄窝,保存有該方法調(diào)用的參數(shù)牵寺、局部變量和返回地址。然而JAVA中的局部變量只能是基本類型變量(int)恩脂,或者對象的引用帽氓。所以在棧中只存放基本類型變量和對象的引用。引用的對象保存在堆中俩块。

當(dāng)某方法運行結(jié)束時黎休,該方法對應(yīng)的frame將會從棧中刪除,frame中所有局部變量和參數(shù)所占有的空間也隨之釋放玉凯。線程回到原方法繼續(xù)執(zhí)行势腮,當(dāng)所有的棧都清空的時候,程序也就隨之運行結(jié)束漫仆。

而對于堆內(nèi)存捎拯,堆存放著普通變量。在JAVA中堆內(nèi)存不會隨著方法的結(jié)束而清空盲厌,所以在方法中定義了局部變量署照,在方法結(jié)束后變量依然存活在堆中。

綜上所述吗浩,棧(stack)可以自行清除不用的內(nèi)存空間藤树。但是如果我們不停的創(chuàng)建新對象,堆(heap)的內(nèi)存空間就會被消耗盡拓萌。所以JAVA引入了垃圾回收(garbage collection岁钓,簡稱GC)去處理堆內(nèi)存的回收,但如果對象一直被引用無法被回收微王,造成內(nèi)存的浪費屡限,無法再被使用。所以對象無法被GC回收就是造成內(nèi)存泄露的原因炕倘!

垃圾回收機(jī)制

垃圾回收(garbage collection钧大,簡稱GC)可以自動清空堆中不再使用的對象。在JAVA中對象是通過引用使用的罩旋。如果再沒有引用指向該對象啊央,那么該對象就無從處理或調(diào)用該對象,這樣的對象稱為不可到達(dá)(unreachable)涨醋。垃圾回收用于釋放不可到達(dá)的對象所占據(jù)的內(nèi)存瓜饥。

實現(xiàn)思想:我們將棧定義為root,遍歷棧中所有的對象的引用浴骂,再遍歷一遍堆中的對象乓土。因為棧中的對象的引用執(zhí)行完畢就刪除,所以我們就可以通過棧中的對象的引用,查找到堆中沒有被指向的對象趣苏,這些對象即為不可到達(dá)對象狡相,對其進(jìn)行垃圾回收。

垃圾回收實現(xiàn)思想

如果持有對象的強(qiáng)引用食磕,垃圾回收器是無法在內(nèi)存中回收這個對象尽棕。

引用類型

在JDK 1.2以前的版本中,若一個對象不被任何變量引用彬伦,那么程序就無法再使用這個對象滔悉。也就是說,只有對象處于可觸及(reachable)狀態(tài)媚朦,程序才能使用它氧敢。從JDK 1.2版本開始,把對象的引用分為4種級別询张,從而使程序能更加靈活地控制對象的生命周期孙乖。這4種級別由高到低依次為:強(qiáng)引用、軟引用份氧、弱引用和虛引用唯袄。
Java/Android引用類型及其使用分析

1. 強(qiáng)引用(Strong reference)
實際編碼中最常見的一種引用類型。常見形式如:A a = new A();等蜗帜。強(qiáng)引用本身存儲在棧內(nèi)存中恋拷,其存儲指向?qū)?nèi)存中對象的地址。一般情況下厅缺,當(dāng)對內(nèi)存中的對象不再有任何強(qiáng)引用指向它時蔬顾,垃圾回收機(jī)器開始考慮可能要對此內(nèi)存進(jìn)行的垃圾回收。如當(dāng)進(jìn)行編碼:a = null湘捎,此時诀豁,剛剛在堆中分配地址并新建的a對象沒有其他的任何引用,當(dāng)系統(tǒng)進(jìn)行垃圾回收時窥妇,堆內(nèi)存將被垃圾回收舷胜。

2. 軟引用(Soft Reference)
軟引用的一般使用形式如下:

A a = new A();
SoftReference<A> srA = new SoftReference<A>(a);

軟引用所指示的對象進(jìn)行垃圾回收需要滿足如下兩個條件:
1.當(dāng)其指示的對象沒有任何強(qiáng)引用對象指向它;
2.當(dāng)虛擬機(jī)內(nèi)存不足時活翩。
因此烹骨,SoftReference變相的延長了其指示對象占據(jù)堆內(nèi)存的時間,直到虛擬機(jī)內(nèi)存不足時垃圾回收器才回收此堆內(nèi)存空間材泄。

3. 弱引用(Weak Reference)
同樣的沮焕,軟引用的一般使用形式如下:

A a = new A();
WeakReference<A> wrA = new WeakReference<A>(a);

WeakReference不改變原有強(qiáng)引用對象的垃圾回收時機(jī),一旦其指示對象沒有任何強(qiáng)引用對象時脸爱,此對象即進(jìn)入正常的垃圾回收流程遇汞。

4. 虛引用(Phantom Reference)
與SoftReference或WeakReference相比,PhantomReference主要差別體現(xiàn)在如下幾點:
1.PhantomReference只有一個構(gòu)造函數(shù)

PhantomReference(T referent, ReferenceQueue<? super T> q)

2.不管有無強(qiáng)引用指向PhantomReference的指示對象簿废,PhantomReference的get()方法返回結(jié)果都是null空入。

因此,PhantomReference使用必須結(jié)合ReferenceQueue族檬;
與WeakReference相同歪赢,PhantomReference并不會改變其指示對象的垃圾回收時機(jī)。

內(nèi)存泄露原因

如果持有對象的強(qiáng)引用单料,垃圾回收器是無法在內(nèi)存中回收這個對象埋凯。

內(nèi)存泄露的真因是:持有對象的強(qiáng)引用,且沒有及時釋放扫尖,進(jìn)而造成內(nèi)存單元一直被占用白对,浪費空間,甚至可能造成內(nèi)存溢出换怖!

其實在Android中會造成內(nèi)存泄露的情景無外乎兩種:
  • 全局進(jìn)程(process-global)的static變量甩恼。這個無視應(yīng)用的狀態(tài),持有Activity的強(qiáng)引用的怪物沉颂。
  • 活在Activity生命周期之外的線程条摸。沒有清空對Activity的強(qiáng)引用。

檢查一下你的項目中是否有以下幾種情況:

  • Static Activities
  • Static Views
  • Inner Classes
  • Anonymous Classes
  • Handler
  • Threads
  • TimerTask
  • Sensor Manager

詳解見該文章《Android內(nèi)存泄漏的八種可能》

最后推薦一個可檢測app內(nèi)存泄露的項目:LeakCanary(可以檢測app的內(nèi)存泄露)

總結(jié)

雖然現(xiàn)在手機(jī)內(nèi)存在不停的提升铸屉,內(nèi)存泄露興許不會像dalvik時代由于虛擬機(jī)內(nèi)存過小造成各種花樣oom钉蒲。但是過量的內(nèi)存泄露依然會造成內(nèi)存溢出,影響用戶體驗彻坛,在如今定制系統(tǒng)層出不窮顷啼、機(jī)型花樣越來越多的情況下解決好內(nèi)存泄露的問題會讓適配和穩(wěn)定性進(jìn)一步提高!

希望我的文章能給大家?guī)硪稽c點的福利昌屉,那在下就足夠開心了钙蒙。
下次再見!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末怠益,一起剝皮案震驚了整個濱河市仪搔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜻牢,老刑警劉巖烤咧,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異抢呆,居然都是意外死亡煮嫌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門抱虐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昌阿,“玉大人,你說我怎么就攤上這事∨潮” “怎么了灶轰?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長刷钢。 經(jīng)常有香客問我笋颤,道長,這世上最難降的妖魔是什么内地? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任伴澄,我火速辦了婚禮,結(jié)果婚禮上阱缓,老公的妹妹穿的比我還像新娘非凌。我一直安慰自己,他們只是感情好荆针,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布敞嗡。 她就那樣靜靜地躺著,像睡著了一般祭犯。 火紅的嫁衣襯著肌膚如雪秸妥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天沃粗,我揣著相機(jī)與錄音粥惧,去河邊找鬼。 笑死最盅,一個胖子當(dāng)著我的面吹牛突雪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播涡贱,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼咏删,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了问词?” 一聲冷哼從身側(cè)響起督函,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎激挪,沒想到半個月后辰狡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡垄分,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年宛篇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片薄湿。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡叫倍,死狀恐怖偷卧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吆倦,我是刑警寧澤听诸,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站逼庞,受9級特大地震影響蛇更,放射性物質(zhì)發(fā)生泄漏瞻赶。R本人自食惡果不足惜赛糟,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望砸逊。 院中可真熱鬧璧南,春花似錦、人聲如沸师逸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽篓像。三九已至动知,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間员辩,已是汗流浹背盒粮。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留奠滑,地道東北人丹皱。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像宋税,于是被迫代替她去往敵國和親摊崭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言 不知道大家有沒有這樣一種感覺,程序員的數(shù)量井噴了乏屯「保可能是因為互聯(lián)網(wǎng)火了,也可能是各家培訓(xùn)機(jī)構(gòu)為我們拉來了大量...
    活這么大就沒飽過閱讀 2,727評論 6 26
  • 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題瓶珊。內(nèi)存泄漏大家都不陌生了啸箫,簡單粗俗的講,...
    DreamFish閱讀 791評論 0 5
  • * js語法* js的動態(tài)函數(shù)和匿名函數(shù)* js動態(tài)函數(shù)Functionnew Function();* 匿名函數(shù)...
    狠哇塞的小伙子啊閱讀 313評論 0 0
  • 1.自控力:把錢投資給高度自控的人伞芹。自控力是高效獲得一切后天能力的基本能力忘苛。對于創(chuàng)業(yè)者而言蝉娜,后天重要的能力就是領(lǐng)導(dǎo)...
    劉書亞的天堂之路閱讀 372評論 0 0
  • 什么是幸福??,我們苦著自己想要給孩子一個幸福的生活的人扎唾,我們的家長召川,其實幸福生活是一種能力。這種能力是學(xué)習(xí)來的胸遇。如...
    晗愛旅閱讀 387評論 0 0