Java 內(nèi)存泄漏

前言:

Java語(yǔ)言的一個(gè)關(guān)鍵的優(yōu)勢(shì)就是它的內(nèi)存管理機(jī)制。你只管創(chuàng)建對(duì)象颂斜,Java的垃圾回收器幫你分配以及回收內(nèi)存夫壁。然而,實(shí)際的情況并沒有那么簡(jiǎn)單沃疮,因?yàn)閮?nèi)存泄漏在Java應(yīng)用程序中還是時(shí)有發(fā)生的盒让。

1. 什么是內(nèi)存泄漏?

????創(chuàng)建的對(duì)象不再被其他應(yīng)用程序使用司蔬,但因?yàn)楸黄渌麑?duì)象所引用著(即通過(guò)可達(dá)性分析糯彬,從GC Roots具有到該對(duì)象的鏈路),因此垃圾回收器沒辦法回收它們葱她。

2. 為什么會(huì)發(fā)生內(nèi)存泄漏撩扒?

????下面這個(gè)例子中,A對(duì)象引用B對(duì)象,A對(duì)象的生命周期(t1-t4)比B對(duì)象的生命周期(t2-t3)長(zhǎng)的多搓谆。當(dāng)B對(duì)象沒有被應(yīng)用程序使用之后炒辉,A對(duì)象仍然在引用著B對(duì)象。這樣泉手,垃圾回收器就沒辦法將B對(duì)象從內(nèi)存中移除黔寇,從而導(dǎo)致內(nèi)存問(wèn)題,因?yàn)槿绻鸄引用更多這樣的對(duì)象斩萌,那將有更多的未被引用對(duì)象存在缝裤,并消耗內(nèi)存空間。B對(duì)象也可能會(huì)持有許多其他的對(duì)象颊郎,那這些對(duì)象同樣也不會(huì)被垃圾回收器回收憋飞。所有這些沒在使用的對(duì)象將持續(xù)的消耗之前分配的內(nèi)存空間。


image.png

3. 常見發(fā)生內(nèi)存泄漏情況

????第二節(jié)說(shuō)到過(guò)姆吭,由于某些生命周期長(zhǎng)的對(duì)象引用了生命周期短的對(duì)象榛做,而此時(shí)生命周期短的對(duì)象并沒有被任何程序使用,依據(jù)jvm虛擬機(jī)規(guī)范内狸,這些沒有被任何程序使用的對(duì)象按理應(yīng)該被垃圾收集器進(jìn)行回收检眯,但由于存在引用,因此沒辦法進(jìn)行回收昆淡。最常見的情景便是靜態(tài)集合類锰瘸。

<1>靜態(tài)集合(全局集合)

????在使用Set、Vector昂灵、HashMap等集合類的時(shí)候需要特別注意避凝,有可能會(huì)發(fā)生內(nèi)存泄漏。當(dāng)這些集合被定義成靜態(tài)的時(shí)候倔既,由于它們的生命周期跟應(yīng)用程序一樣長(zhǎng)恕曲,此時(shí),若往靜態(tài)集合類中存放創(chuàng)建的java對(duì)象時(shí)渤涌,很可能發(fā)生內(nèi)存泄漏佩谣。示例代碼:

package com.lm.jvm;

import java.util.HashSet;
import java.util.Set;

/**
 * @author lm
 * @create 2018-10-12 21:28
 * @desc java內(nèi)存泄漏1:靜態(tài)集合類
 **/
public class MemoryLeak {
    static Set<Object> set = new HashSet<>();
    int size;

    public void initSet(){
        for (int i = 0; i < size; i++) {
            Object o = new Object();
            set.add(o);
            o = null;
        }
    }
}

????如上圖代碼所示,循環(huán)創(chuàng)建了Object對(duì)象实蓬,并添加到靜態(tài)集合Set中茸俭,雖然將對(duì)象設(shè)置為null(不再使用),但由于靜態(tài)成員變量生命周期與類的生命周期一致安皱,即生命周期長(zhǎng)的對(duì)象引用著不再被任何程序使用的生命周期短的對(duì)象调鬓,因此這些本該要被回收的對(duì)象并不能被GC,因此造成了內(nèi)存泄漏酌伊。

<2>監(jiān)聽器

????在Java中腾窝,我們經(jīng)常會(huì)使用到監(jiān)聽器,如對(duì)某個(gè)控件添加單擊監(jiān)聽器addOnClickListener(),但往往釋放對(duì)象的時(shí)候會(huì)忘記刪除監(jiān)聽器虹脯,這就有可能造成內(nèi)存泄漏驴娃。好的方法就是,在釋放對(duì)象的時(shí)候循集,應(yīng)該記住釋放所有監(jiān)聽器唇敞,這就能避免了因?yàn)楸O(jiān)聽器而導(dǎo)致的內(nèi)存泄漏。

<3>各種連接

????Java中的連接包括數(shù)據(jù)庫(kù)連接咒彤、網(wǎng)絡(luò)連接和IO連接疆柔,如果沒有顯式調(diào)用其close()方法,是不會(huì)自動(dòng)關(guān)閉的镶柱,這些連接就不能被GC回收而導(dǎo)致內(nèi)存泄漏旷档。一般情況下,在try代碼塊里創(chuàng)建連接奸例,在finally里釋放連接彬犯,就能夠避免此類內(nèi)存泄漏向楼。

<4>外部模塊的引用

????調(diào)用外部模塊的時(shí)候查吊,也應(yīng)該注意防止內(nèi)存泄漏。如模塊A調(diào)用了外部模塊B的一個(gè)方法湖蜕,如:
public void register(Object o)
這個(gè)方法有可能就使得A模塊持有傳入對(duì)象的引用逻卖,這時(shí)候需要查看B模塊是否提供了去除引用的方法,如unregister()昭抒。這種情況容易忽略评也,而且發(fā)生了內(nèi)存泄漏的話,比較難察覺灭返,應(yīng)該在編寫代碼過(guò)程中就應(yīng)該注意此類問(wèn)題盗迟。

<5> 單例模式

????使用單例模式的時(shí)候也有可能導(dǎo)致內(nèi)存泄漏。因?yàn)閱卫龑?duì)象初始化后將在JVM的整個(gè)生命周期內(nèi)存在熙含,如果它持有一個(gè)外部對(duì)象(生命周期比較短)的引用罚缕,那么這個(gè)外部對(duì)象就不能被回收,而導(dǎo)致內(nèi)存泄漏怎静。如果這個(gè)外部對(duì)象還持有其它對(duì)象的引用邮弹,那么內(nèi)存泄漏會(huì)更嚴(yán)重,因此需要特別注意此類情況蚓聘。這種情況就需要考慮下單例模式的設(shè)計(jì)會(huì)不會(huì)有問(wèn)題腌乡,應(yīng)該怎樣保證不會(huì)產(chǎn)生內(nèi)存泄漏問(wèn)題。

<6>緩存

????緩存一種用來(lái)快速查找已經(jīng)執(zhí)行過(guò)的操作結(jié)果的數(shù)據(jù)結(jié)構(gòu)夜牡。因此与纽,如果一個(gè)操作執(zhí)行需要比較多的資源并會(huì)多次被使用,通常做法是把常用的輸入數(shù)據(jù)的操作結(jié)果進(jìn)行緩存,以便在下次調(diào)用該操作時(shí)使用緩存的數(shù)據(jù)急迂。緩存通常都是以動(dòng)態(tài)方式實(shí)現(xiàn)的,如果緩存設(shè)置不正確而大量使用緩存的話則會(huì)出現(xiàn)內(nèi)存溢出的后果硝岗,因此需要將所使用的內(nèi)存容量與檢索數(shù)據(jù)的速度加以平衡。
????常用的解決途徑是使用java.lang.ref.SoftReference類堅(jiān)持將對(duì)象放入緩存袋毙。這個(gè)方法可以保證當(dāng)虛擬機(jī)用完內(nèi)存或者需要更多堆的時(shí)候型檀,可以釋放這些對(duì)象的引用。

<7> 類裝載器

????Java類裝載器的使用為內(nèi)存泄漏提供了許多可乘之機(jī)听盖。一般來(lái)說(shuō)類裝載器都具有復(fù)雜結(jié)構(gòu)胀溺,因?yàn)轭愌b載器不僅僅是只與"常規(guī)"對(duì)象引用有關(guān),同時(shí)也和對(duì)象內(nèi)部的引用有關(guān)皆看。比如數(shù)據(jù)變量仓坞,方法和各種類。這意味著只要存在對(duì)數(shù)據(jù)變量腰吟,方法无埃,各種類和對(duì)象的類裝載器,那么類裝載器將駐留在JVM中毛雇。既然類裝載器可以同很多的類關(guān)聯(lián)嫉称,同時(shí)也可以和靜態(tài)數(shù)據(jù)變量關(guān)聯(lián),那么相當(dāng)多的內(nèi)存就可能發(fā)生泄漏灵疮。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末织阅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子震捣,更是在濱河造成了極大的恐慌荔棉,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蒿赢,死亡現(xiàn)場(chǎng)離奇詭異润樱,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)羡棵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門壹若,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人晾腔,你說(shuō)我怎么就攤上這事舌稀。” “怎么了灼擂?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵壁查,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我剔应,道長(zhǎng)睡腿,這世上最難降的妖魔是什么语御? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮席怪,結(jié)果婚禮上应闯,老公的妹妹穿的比我還像新娘。我一直安慰自己挂捻,他們只是感情好碉纺,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著刻撒,像睡著了一般骨田。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上声怔,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天态贤,我揣著相機(jī)與錄音,去河邊找鬼醋火。 笑死悠汽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的芥驳。 我是一名探鬼主播柿冲,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼晚树!你這毒婦竟也來(lái)了姻采?” 一聲冷哼從身側(cè)響起雅采,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤爵憎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后婚瓜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宝鼓,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年巴刻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了愚铡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡胡陪,死狀恐怖沥寥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情柠座,我是刑警寧澤邑雅,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站妈经,受9級(jí)特大地震影響淮野,放射性物質(zhì)發(fā)生泄漏捧书。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一骤星、第九天 我趴在偏房一處隱蔽的房頂上張望经瓷。 院中可真熱鬧,春花似錦洞难、人聲如沸舆吮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)歪泳。三九已至,卻和暖如春露筒,著一層夾襖步出監(jiān)牢的瞬間呐伞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工慎式, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留伶氢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓瘪吏,卻偏偏與公主長(zhǎng)得像癣防,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子掌眠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

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