Effective.Java 讀書筆記(6)內(nèi)存泄漏

6.Eliminate obsolete object reference

大意為 消除舊的對象引用

當(dāng)你使用直接操作內(nèi)存的語言,例如C或者C++的時候,一些內(nèi)存釋放的操作會比較麻煩孙咪,而我們使用java這一種擁有垃圾回收機制的語言的時候落蝙,這份工作就變得輕松多了母廷,但是要注意的是斑举,這個垃圾回收機制并不能讓我們對于內(nèi)存管理掉以輕心

考慮一下下面這個棧類型的實現(xiàn)

// Can you spot the "memory leak"?
public class Stack {
  private Object[] elements;
  private int size = 0;
  private static final int DEFAULT_INITIAL_CAPACITY = 16;

  public Stack() {
       elements = new Object[DEFAULT_INITIAL_CAPACITY];
  }

  public void push(Object e) {
    ensureCapacity();
    elements[size++] = e;
  }

  public Object pop() {
    if (size == 0)
         throw new EmptyStackException();
    return elements[--size];
  }

/**
* Ensure space for at least one more element, roughly
* doubling the capacity each time the array needs to grow.
*/
  private void ensureCapacity() {
    if (elements.length == size)
         elements = Arrays.copyOf(elements, 2 * size + 1);
    }
}

看上去好像沒有什么明顯的問題,測試起來也都可以通過對吧脊奋,但是注意到其中潛伏的一個問題熬北,寬泛點說,這個程序有著內(nèi)存泄漏的風(fēng)險诚隙,這樣的風(fēng)險會導(dǎo)致垃圾回收的壓力增大并且加大內(nèi)存的開銷從而降低整個程序性能讶隐,最嚴重的時候可能會產(chǎn)生OutOfMemoryError的錯誤,但是這樣的錯誤比較少見

那么是那一部分內(nèi)存泄漏呢久又,其實就是pop彈出棧操作中stack仍然保留著已經(jīng)彈出的element的引用巫延,那樣垃圾回收機制并不會去回收,并且這樣的一個舊的引用并不會被重引用籽孙,即使我們的stack沒有所有element的引用了烈评,垃圾回收機制也不會去回收,由于stack一直維持著一個舊的引用

內(nèi)存泄漏在擁有垃圾回收機制(更加適合的說法是犯建,無意的對象保留)的語言里面是十分陰險的讲冠,如果一個對象的引用無意間保留了下來,不僅僅這個對象不會被垃圾回收适瓦,那些被這個對象所引用的對象也不能被回收竿开,鏈式效應(yīng)會使得整個程序的性能極具下降

為了解決這樣的一個問題,我們只需要簡單地把那些引用置為null就可以了玻熙,比如在上述程序中否彩,我們只要這樣修改就好

public Object pop() {
  if (size == 0)
       throw new EmptyStackException();
  Object result = elements[--size];
  elements[size] = null; // Eliminate obsolete reference
  return result;
}

只要你重引用這個元素,系統(tǒng)就會拋出一個空指針異常嗦随,這對于檢測程序異常錯誤十分常見

當(dāng)程序員第一次面臨這個問題的時候列荔,他們可能會在程序結(jié)束使用的時候過度去置空每一個對象的引用敬尺,這樣是既沒有必要又不被期望的,這樣會使得代碼變得雜亂贴浙,置空對象的引用應(yīng)該視情況而定而不是規(guī)范式地照搬砂吞,關(guān)于消除舊引用的最好的辦法就是讓這些包含引用的變量越界,這經(jīng)常在你在一個狹窄的范圍內(nèi)定義一個變量的時候會發(fā)生

那么崎溃,什么時候我們?nèi)ブ每者@些引用呢蜻直?簡單來說,當(dāng)你的類中所擁有的變量的引用沒有重用的可能并且你的類還繼續(xù)擁有著這個引用的話袁串,就把它置為空就可以了概而,如果不置為空,垃圾回收機制并不知道這個引用沒有作用了囱修,也就不會去回收了

總而言之赎瑰,只要當(dāng)一個類管理它自己的內(nèi)存,程序員就應(yīng)該注意一下內(nèi)存泄漏的風(fēng)險蔚袍,當(dāng)一個元素是free的時候乡范,任何對這個元素的引用都應(yīng)該被置空

另一個比較常見的可能造成內(nèi)存泄漏的原因就是緩存了配名,一旦你把一個對象的引用放到緩存里面啤咽,很容易忘記它在緩存那里并且很容易就把它一直放在緩存那里知道它變得完全沒有作用了,對于這類問題渠脉,有著一些解決方案宇整,如果你足夠幸運,實現(xiàn)的緩存的條目都是完全相關(guān)的并且只要對于鍵值存在緩存外部的引用芋膘,代表性的緩存例如WeakHashMap鳞青,一旦條目變過時了就會自動被移除,記住WeakHashMap为朋,這個類很有用當(dāng)緩存條目的生存時間取決于外部對于鍵值的引用臂拓,而不是值的引用的時候

更加常見的是,一個緩存條目的有用的生存時間很少被定義的很好习寸,隨著時間條目變得越來越?jīng)]有價值胶惰,在這種情況下,緩存應(yīng)該偶爾清一下那些不用了的條目霞溪,這可以利用后臺線程來處理(可能是一個Timer 或者是一個ScheduledThreadPoolExecutor )或者添加新的條目的時候就會有這種作用孵滞,LinkedHashMap類使用了它的removeEldestEntry方法使得后一種方法更加簡便,對于更加復(fù)雜的緩存鸯匹,你可能需要直接的使用 java.lang.ref

第三種常見的內(nèi)存泄漏的就是監(jiān)聽器和其他的回調(diào)坊饶,如果你實現(xiàn)了一個API,這個API是當(dāng)用戶注冊回調(diào)但是并沒有明確的解除注冊殴蓬,他們會積累起來除非你采取某些措施匿级,最好的辦法來保正這些回調(diào)被垃圾回收及時處理就是只儲存weak reference(弱引用),對于實例,就利用WeakHashMap儲存他們作為鍵

因為內(nèi)存泄漏不會特別的明顯地顯示出來痘绎,他們可能在某個系統(tǒng)里面潛藏很久脓杉,只有十分仔細的代碼或者debug工具(比如heap profiler)的輔助才能發(fā)現(xiàn)他們,因此简逮,我們需要學(xué)習(xí)去在這些問題發(fā)生之前預(yù)測并且防止他們發(fā)生

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末球散,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子散庶,更是在濱河造成了極大的恐慌蕉堰,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悲龟,死亡現(xiàn)場離奇詭異屋讶,居然都是意外死亡,警方通過查閱死者的電腦和手機须教,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門皿渗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人轻腺,你說我怎么就攤上這事乐疆。” “怎么了贬养?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵挤土,是天一觀的道長。 經(jīng)常有香客問我误算,道長仰美,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任儿礼,我火速辦了婚禮咖杂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蚊夫。我一直安慰自己诉字,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布这橙。 她就那樣靜靜地躺著奏窑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪屈扎。 梳的紋絲不亂的頭發(fā)上埃唯,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天,我揣著相機與錄音鹰晨,去河邊找鬼墨叛。 笑死止毕,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的漠趁。 我是一名探鬼主播扁凛,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼闯传!你這毒婦竟也來了谨朝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤甥绿,失蹤者是張志新(化名)和其女友劉穎字币,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體共缕,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡洗出,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了图谷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翩活。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖便贵,靈堂內(nèi)的尸體忽然破棺而出菠镇,到底是詐尸還是另有隱情,我是刑警寧澤嫉沽,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布辟犀,位于F島的核電站俏竞,受9級特大地震影響绸硕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜魂毁,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一玻佩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧席楚,春花似錦咬崔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至只祠,卻和暖如春兜蠕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抛寝。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工熊杨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留曙旭,地道東北人。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓晶府,卻偏偏與公主長得像桂躏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子川陆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,647評論 2 354

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

  • Java引用的種類 1.對象在內(nèi)存中的狀態(tài) 對于JVM的垃圾回收機制來說剂习,是否回收一個對象的標(biāo)準在于:是否還有引用...
    Jack921閱讀 3,855評論 0 3
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法较沪,內(nèi)部類的語法进倍,繼承相關(guān)的語法,異常的語法购对,線程的語...
    子非魚_t_閱讀 31,623評論 18 399
  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題猾昆。內(nèi)存泄漏...
    _痞子閱讀 1,634評論 0 8
  • 最近正在熟悉Java內(nèi)存泄漏的相關(guān)知識,上網(wǎng)查閱了一些資料骡苞,在此做個整理算是對收獲的一些總結(jié)垂蜗,希望能對各位有所幫助...
    李序鍇閱讀 28,870評論 4 62
  • 內(nèi)存管理的目的就是讓我們在開發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問題。內(nèi)存泄漏大家都不陌生了解幽,簡單粗俗的講贴见,...
    宇宙只有巴掌大閱讀 2,362評論 0 12