很多人可能在想這么一個(gè)問題:Java有垃圾回收機(jī)制嗤栓,那么還存在內(nèi)存泄露嗎?答案是肯定的精续,所謂的垃圾回收GC會(huì)自動(dòng)管理內(nèi)存的回收箕肃,而不需要程序員每次都手動(dòng)釋放內(nèi)存婚脱,但是如果存在大量的臨時(shí)對(duì)象在不需要使用時(shí)并沒有取消對(duì)它們的引用,就會(huì)吞噬掉大量的內(nèi)存勺像,很快就會(huì)造成內(nèi)存溢出障贸。
一、Java的垃圾回收機(jī)制
Java中的對(duì)象是在堆中分配吟宦,對(duì)象的創(chuàng)建有2中方式:new或者反射篮洁。對(duì)象的回收是通過垃圾收集器,JVM的垃圾收集器簡(jiǎn)化了程序員的工作督函,但是卻加重了JVM的工作嘀粱,這是Java程序運(yùn)行稍慢的原因之一激挪,因?yàn)镚C為了能正確釋放對(duì)象,必須監(jiān)控每一個(gè)對(duì)象的運(yùn)行狀態(tài)锋叨,包括對(duì)象的申請(qǐng)垄分、引用、被引用娃磺、賦值等薄湿,GC都要進(jìn)行監(jiān)控,監(jiān)控對(duì)象的狀態(tài)是為了更加準(zhǔn)確偷卧、及時(shí)地釋放對(duì)象豺瘤,而釋放對(duì)象的根本原則就是該對(duì)象不再被引用。
三听诸、Java中的內(nèi)存泄露
內(nèi)存泄露的對(duì)象有以下兩個(gè)特點(diǎn):
① 這些對(duì)象是可達(dá)的坐求,即在有向圖中存在通路可以與其相連。
② 這些對(duì)象是無用的晌梨,即程序以后都不會(huì)再使用這些對(duì)象桥嗤。
public class Stack {
private final static int MAX_ATTRIBUTE = 10;
private Object[] arr;
private int index = 0;
public void push(Object obj) {
if (index > 9)
throw new IllegalArgumentException();
arr[index] = obj;
index++;
}
public Stack() {
arr = new Object[MAX_ATTRIBUTE];
}
public Object pop() {
if (index < 0)
throw new IllegalArgumentException();
return arr[--index];
}
}
這個(gè)程序那里發(fā)生了內(nèi)存泄露呢?如果一個(gè)棧先增長(zhǎng)然后收縮仔蝌,那么從棧中彈出來的對(duì)象將不會(huì)被當(dāng)做垃圾回收泛领,即使使用棧的程序不再引用這些對(duì)象,它們也不會(huì)被回收敛惊,因?yàn)闂?nèi)部維護(hù)這對(duì)這些對(duì)象的過期引用渊鞋。
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; //Eliminate obsolete reference
return result;
}
解決辦法:只要一個(gè)元素被彈出棧,那么就將它的引用置為空瞧挤,GC就會(huì)回收锡宋。即一旦數(shù)組元素變成了非活動(dòng)部分的一部分,就手工清空這些數(shù)組元素皿伺。
不要被類似的問題困擾
當(dāng)程序員第一次被類似這樣的問題困擾的時(shí)候员辩,他們往往會(huì)過分小心盒粮;對(duì)于每一個(gè)對(duì)象引用鸵鸥,一旦不再使用它,就把它清空丹皱,這是沒有必要的妒穴,這樣反而會(huì)把代碼弄的混亂。清空對(duì)象的引用應(yīng)該是一種例外(因?yàn)樗鼉H在一些特殊的情況下才需要進(jìn)行)摊崭,而不是一種規(guī)范行為讼油。消除過期引用最好的辦法是讓包含該引用的變量結(jié)束其生命周期。如果是在最緊湊的作用域范圍內(nèi)定義每一個(gè)變量呢簸,這種情況就會(huì)自然而然地發(fā)生矮台。
一般而言乏屯,只要類是自己管理內(nèi)存,就應(yīng)該警惕內(nèi)存泄漏問題瘦赫。一旦元素被釋放掉辰晕,則該元素中包含的任何對(duì)象引用都應(yīng)該被清空。
四确虱、常見的內(nèi)存泄露
① 緩存含友。一旦你把對(duì)象引用放到緩存中,它就很容易被遺忘掉校辩,從而使得它不再有用之后很長(zhǎng)一段時(shí)間內(nèi)仍然留在緩存中窘问。
② 在Java程序中,我們經(jīng)常要和監(jiān)聽器打交道宜咒,通常調(diào)用諸如addXXXListener()等方法來增加監(jiān)聽器惠赫,但往往忘記刪除這些監(jiān)聽器,從而增加了內(nèi)存泄漏的機(jī)會(huì)故黑。
③ 使用連接池時(shí)汉形,除了要顯式地關(guān)閉連接,還必須顯式地關(guān)閉Resultset和Statement對(duì)象倍阐。否則會(huì)造成大量的這些對(duì)象無法釋放概疆,從而引起內(nèi)存泄露。
④ 盡量少用靜態(tài)變量峰搪,因?yàn)殪o態(tài)變量存放在永久代岔冀,永久代基本不參與垃圾回收