Java內(nèi)存、Android 內(nèi)存泄漏

轉(zhuǎn)載自:http://blog.csdn.net/codeemperor/article/details/51514448媳搪,轉(zhuǎn)載請備注洼冻。

Java 內(nèi)存分配策略

Java 程序運行時的內(nèi)存分配策略有三種,分別是靜態(tài)分配,棧式分配,和堆式分配宣虾,對應(yīng)的惯裕,三種存儲策略使用的內(nèi)存空間主要分別是靜態(tài)存儲區(qū)(也稱方法區(qū))、棧區(qū)和堆區(qū)安岂。

靜態(tài)存儲區(qū)(方法區(qū)):主要存放靜態(tài)數(shù)據(jù)、全局 static 數(shù)據(jù)和常量帆吻。這塊內(nèi)存在程序編譯時就已經(jīng)分配好域那,并且在程序整個運行期間都存在。

棧區(qū) :當方法被執(zhí)行時,方法體內(nèi)的局部變量都在棧上創(chuàng)建次员,并在方法執(zhí)行結(jié)束時這些局部變量所持有的內(nèi)存將會自動被釋放败许。因為棧內(nèi)存分配運算內(nèi)置于處理器的指令集中,效率很高淑蔚,但是分配的內(nèi)存容量有限市殷。

堆區(qū) : 又稱動態(tài)內(nèi)存分配,通常就是指在程序運行時直接 new 出來的內(nèi)存刹衫。這部分內(nèi)存在不使用時將會由 Java 垃圾回收器來負責回收醋寝。

棧與堆的區(qū)別:

在方法體內(nèi)定義的(局部變量)一些基本類型的變量和對象的引用變量都是在方法的棧內(nèi)存中分配的。當在一段方法塊中定義一個變量時带迟,Java 就會在棧中為該變量分配內(nèi)存空間音羞,當超過該變量的作用域后,該變量也就無效了仓犬,分配給它的內(nèi)存空間也將被釋放掉嗅绰,該內(nèi)存空間可以被重新使用。

堆內(nèi)存用來存放所有由 new 創(chuàng)建的對象(包括該對象其中的所有成員變量)和數(shù)組搀继。在堆中分配的內(nèi)存窘面,將由 Java 垃圾回收器來自動管理。在堆中產(chǎn)生了一個數(shù)組或者對象后叽躯,還可以在棧中定義一個特殊的變量财边,這個變量的取值等于數(shù)組或者對象在堆內(nèi)存中的首地址,這個特殊的變量就是我們上面說的引用變量险毁。我們可以通過這個引用變量來訪問堆中的對象或者數(shù)組制圈。

結(jié)論:

局部變量的基本數(shù)據(jù)類型和引用存儲于棧中,引用的對象實體存儲于堆中畔况【校—— 因為它們屬于方法中的變量,生命周期隨方法而結(jié)束跷跪。

成員變量全部存儲與堆中(包括基本數(shù)據(jù)類型馋嗜,引用和引用的對象實體)—— 因為它們屬于類,類對象終究是要被new出來使用的吵瞻。

了解了 Java 的內(nèi)存分配之后葛菇,我們再來看看 Java 是怎么管理內(nèi)存的。

Java是如何管理內(nèi)存

Java的內(nèi)存管理就是對象的分配和釋放問題橡羞。在 Java 中眯停,程序員需要通過關(guān)鍵字 new 為每個對象申請內(nèi)存空間 (基本類型除外),所有的對象都在堆 (Heap)中分配空間卿泽。另外莺债,對象的釋放是由 GC 決定和執(zhí)行的。在 Java 中,內(nèi)存的分配是由程序完成的齐邦,而內(nèi)存的釋放是由 GC 完成的椎侠,這種收支兩條線的方法確實簡化了程序員的工作。但同時措拇,它也加重了JVM的工作我纪。這也是 Java 程序運行速度較慢的原因之一。因為丐吓,GC 為了能夠正確釋放對象浅悉,GC 必須監(jiān)控每一個對象的運行狀態(tài),包括對象的申請汰蜘、引用仇冯、被引用、賦值等族操,GC 都需要進行監(jiān)控苛坚。

監(jiān)視對象狀態(tài)是為了更加準確地、及時地釋放對象色难,而釋放對象的根本原則就是該對象不再被引用泼舱。

Java中的內(nèi)存泄漏

1.Java內(nèi)存回收機制

不論哪種語言的內(nèi)存分配方式,都需要返回所分配內(nèi)存的真實地址枷莉,也就是返回一個指針到內(nèi)存塊的首地址娇昙。Java中對象是采用new或者反射的方法創(chuàng)建的,這些對象的創(chuàng)建都是在堆(Heap)中分配的笤妙,所有對象的回收都是由Java虛擬機通過垃圾回收機制完成的冒掌。GC為了能夠正確釋放對象,會監(jiān)控每個對象的運行狀況蹲盘,對他們的申請股毫、引用、被引用召衔、賦值等狀況進行監(jiān)控铃诬,Java會使用有向圖的方法進行管理內(nèi)存,實時監(jiān)控對象是否可以達到苍凛,如果不可到達趣席,則就將其回收,這樣也可以消除引用循環(huán)的問題醇蝴。在Java語言中宣肚,判斷一個內(nèi)存空間是否符合垃圾收集標準有兩個:一個是給對象賦予了空值null,以下再沒有調(diào)用過悠栓,另一個是給對象賦予了新值霉涨,這樣重新分配了內(nèi)存空間弧呐。

2.Java內(nèi)存泄漏引起的原因

內(nèi)存泄漏是指無用對象(不再使用的對象)持續(xù)占有內(nèi)存或無用對象的內(nèi)存得不到及時釋放,從而造成內(nèi)存空間的浪費稱為內(nèi)存泄漏嵌纲。內(nèi)存泄露有時不嚴重且不易察覺,這樣開發(fā)者就不知道存在內(nèi)存泄露腥沽,但有時也會很嚴重逮走,會提示你Out of memory。

Java內(nèi)存泄漏的根本原因是什么呢今阳?長生命周期的對象持有短生命周期對象的引用就很可能發(fā)生內(nèi)存泄漏师溅,盡管短生命周期對象已經(jīng)不再需要,但是因為長生命周期持有它的引用而導(dǎo)致不能被回收盾舌,這就是Java中內(nèi)存泄漏的發(fā)生場景墓臭。具體主要有如下幾大類:

1、靜態(tài)集合類引起內(nèi)存泄漏:

2妖谴、當集合里面的對象屬性被修改后窿锉,再調(diào)用remove()方法時不起作用。

3膝舅、監(jiān)聽器

4嗡载、各種連接

5、內(nèi)部類和外部模塊的引用

6仍稀、單例模式

Android中常見的內(nèi)存泄漏匯總

集合類泄漏

單例造成的內(nèi)存泄漏

匿名內(nèi)部類/非靜態(tài)內(nèi)部類和異步線程

這樣就在Activity內(nèi)部創(chuàng)建了一個非靜態(tài)內(nèi)部類的單例洼滚,每次啟動Activity時都會使用該單例的數(shù)據(jù),這樣雖然避免了資源的重復(fù)創(chuàng)建技潘,不過這種寫法卻會造成內(nèi)存泄漏遥巴,因為非靜態(tài)內(nèi)部類默認會持有外部類的引用,而該非靜態(tài)內(nèi)部類又創(chuàng)建了一個靜態(tài)的實例享幽,該實例的生命周期和應(yīng)用的一樣長铲掐,這就導(dǎo)致了該靜態(tài)實例一直會持有該Activity的引用,導(dǎo)致Activity的內(nèi)存資源不能正沉鹕粒回收迹炼。

Handler 造成的內(nèi)存泄漏

Handler 的使用造成的內(nèi)存泄漏問題應(yīng)該說是最為常見了,很多時候我們?yōu)榱吮苊?ANR 而不在主線程進行耗時操作颠毙,在處理網(wǎng)絡(luò)任務(wù)或者封裝一些請求回調(diào)等api都借助Handler來處理斯入,但 Handler 不是萬能的,對于 Handler 的使用代碼編寫一不規(guī)范即有可能造成內(nèi)存泄漏蛀蜜。另外刻两,我們知道 Handler、Message 和 MessageQueue 都是相互關(guān)聯(lián)在一起的,萬一 Handler 發(fā)送的 Message 尚未被處理,則該 Message 及發(fā)送它的 Handler 對象將被線程 MessageQueue 一直持有翻默。

由于 Handler 屬于 TLS(Thread Local Storage) 變量, 生命周期和 Activity 是不一致的咳榜。因此這種實現(xiàn)方式一般很難保證跟 View 或者 Activity 的生命周期保持一致盯质,故很容易導(dǎo)致無法正確釋放嫁赏。

在 Activity 中避免使用非靜態(tài)內(nèi)部類悔橄,比如上面我們將 Handler 聲明為靜態(tài)的蒸其,則其存活期跟 Activity 的生命周期就無關(guān)了帝美。同時通過弱引用的方式引入 Activity碍彭,避免直接將 Activity 作為 context 傳進去。

綜述悼潭,即推薦使用靜態(tài)內(nèi)部類 + WeakReference 這種方式庇忌。每次使用前注意判空。

前面提到了 WeakReference舰褪,所以這里就簡單的說一下 Java 對象的幾種引用類型皆疹。

Java對引用的分類有 Strong reference, SoftReference, WeakReference, PhatomReference 四種。

Java引用的分類

使用軟引用以后占拍,在OutOfMemory異常發(fā)生之前略就,這些緩存的圖片資源的內(nèi)存空間可以被釋放掉的,從而避免內(nèi)存達到上限晃酒,避免Crash發(fā)生残制。

如果只是想避免OutOfMemory異常的發(fā)生,則可以使用軟引用掖疮。如果對于應(yīng)用的性能更在意初茶,想盡快回收一些占用內(nèi)存比較大的對象,則可以使用弱引用浊闪。

另外可以根據(jù)對象是否經(jīng)常使用來判斷選擇軟引用還是弱引用恼布。如果該對象可能會經(jīng)常使用的,就盡量用軟引用搁宾。如果該對象不被使用的可能性更大些折汞,就可以用弱引用。

創(chuàng)建一個靜態(tài)Handler內(nèi)部類盖腿,然后對 Handler 持有的對象使用弱引用爽待,這樣在回收時也可以回收 Handler 持有的對象,但是這樣做雖然避免了 Activity 泄漏翩腐,不過 Looper 線程的消息隊列中還是可能會有待處理的消息鸟款,所以我們在 Activity 的 Destroy 時或者 Stop 時應(yīng)該移除消息隊列 MessageQueue 中的消息。

盡量避免使用 static 成員變量

避免 override finalize()

資源未關(guān)閉造成的內(nèi)存泄漏

一些不良代碼造成的內(nèi)存壓力

有些代碼并不造成內(nèi)存泄露茂卦,但是它們何什,或是對沒使用的內(nèi)存沒進行有效及時的釋放,或是沒有有效的利用已有的對象而是頻繁的申請新內(nèi)存等龙。

比如:

Bitmap 沒調(diào)用 recycle()方法处渣,對于 Bitmap 對象在不使用時,我們應(yīng)該先調(diào)用 recycle() 釋放內(nèi)存伶贰,然后才它設(shè)置為 null. 因為加載 Bitmap 對象的內(nèi)存空間,一部分是 java 的罐栈,一部分 C 的(因為 Bitmap 分配的底層是通過 JNI 調(diào)用的 )黍衙。 而這個 recyle() 就是針對 C 部分的內(nèi)存釋放。

構(gòu)造 Adapter 時荠诬,沒有使用緩存的 convertView ,每次都在創(chuàng)建新的 converView们豌。這里推薦使用 ViewHolder。

總結(jié)

對 Activity 等組件的引用應(yīng)該控制在 Activity 的生命周期之內(nèi)浅妆; 如果不能就考慮使用 getApplicationContext 或者 getApplication,以避免 Activity 被外部長生命周期的對象引用而泄露障癌。

盡量不要在靜態(tài)變量或者靜態(tài)內(nèi)部類中使用非靜態(tài)外部成員變量(包括context )凌外,即使要使用,也要考慮適時把外部成員變量置空涛浙;也可以在內(nèi)部類中使用弱引用來引用外部類的變量康辑。

對于生命周期比Activity長的內(nèi)部類對象,并且內(nèi)部類中使用了外部類的成員變量轿亮,可以這樣做避免內(nèi)存泄漏:

將內(nèi)部類改為靜態(tài)內(nèi)部類

靜態(tài)內(nèi)部類中使用弱引用來引用外部類的成員變量

Handler 的持有的引用對象最好使用弱引用疮薇,資源釋放時也可以清空 Handler 里面的消息。比如在 Activity onStop 或者 onDestroy 的時候我注,取消掉該 Handler 對象的 Message和 Runnable.

在 Java 的實現(xiàn)過程中按咒,也要考慮其對象釋放,最好的方法是在不使用某對象時但骨,顯式地將此對象賦值為 null励七,比如使用完Bitmap 后先調(diào)用 recycle(),再賦為null,清空對圖片等資源有直接引用或者間接引用的數(shù)組(使用 array.clear() ; array = null)等奔缠,最好遵循誰創(chuàng)建誰釋放的原則掠抬。

正確關(guān)閉資源,對于使用了BraodcastReceiver校哎,ContentObserver两波,F(xiàn)ile,游標 Cursor闷哆,Stream腰奋,Bitmap等資源的使用,應(yīng)該在Activity銷毀時及時關(guān)閉或者注銷抱怔。

保持對對象生命周期的敏感氛堕,特別注意單例、靜態(tài)對象野蝇、全局性集合等的生命周期讼稚。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末括儒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子锐想,更是在濱河造成了極大的恐慌帮寻,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赠摇,死亡現(xiàn)場離奇詭異固逗,居然都是意外死亡,警方通過查閱死者的電腦和手機藕帜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門烫罩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人洽故,你說我怎么就攤上這事贝攒。” “怎么了时甚?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵隘弊,是天一觀的道長。 經(jīng)常有香客問我荒适,道長梨熙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任刀诬,我火速辦了婚禮咽扇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘陕壹。我一直安慰自己肌割,他們只是感情好,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布帐要。 她就那樣靜靜地躺著把敞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪榨惠。 梳的紋絲不亂的頭發(fā)上奋早,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音赠橙,去河邊找鬼耽装。 笑死,一個胖子當著我的面吹牛期揪,可吹牛的內(nèi)容都是我干的掉奄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼凤薛,長吁一口氣:“原來是場噩夢啊……” “哼姓建!你這毒婦竟也來了诞仓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤速兔,失蹤者是張志新(化名)和其女友劉穎墅拭,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涣狗,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡谍婉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了镀钓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片穗熬。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖丁溅,靈堂內(nèi)的尸體忽然破棺而出唤蔗,到底是詐尸還是另有隱情,我是刑警寧澤唧瘾,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站别凤,受9級特大地震影響饰序,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜规哪,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一求豫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诉稍,春花似錦蝠嘉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至服爷,卻和暖如春杜恰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背仍源。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工心褐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人笼踩。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓逗爹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嚎于。 傳聞我的和親對象是個殘疾皇子掘而,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

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

  • 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題挟冠。內(nèi)存泄漏大家都不陌生了,簡單粗俗的講镣屹,...
    宇宙只有巴掌大閱讀 2,363評論 0 12
  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題圃郊。內(nèi)存泄漏...
    _痞子閱讀 1,637評論 0 8
  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題。內(nèi)存泄漏...
    apkcore閱讀 1,221評論 2 7
  • 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題女蜈。內(nèi)存泄漏大家都不陌生了持舆,簡單粗俗的講,...
    DreamFish閱讀 792評論 0 5
  • 文/西西 身后靜靜幽谷 花落成了河 我不知道此中還有 多少山川隱沒的情節(jié) 我與光陰落坐 讓它細細聽我 風過 聽你恍...
    花語清溪閱讀 308評論 4 8