版權(quán)聲明
作者:zuoxiaolong(左瀟龍)
出處:博客園左瀟龍的技術(shù)博客--http://www.cnblogs.com/zuoxiaolong
您的支持是對博主最大的鼓勵针炉,感謝您的認真閱讀挠他。
本文版權(quán)歸作者所有,歡迎轉(zhuǎn)載篡帕,但未經(jīng)作者同意必須保留此段聲明殖侵,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利镰烧。
原文鏈接:http://www.cnblogs.com/zuoxiaolong/p/jvm2.html
各位園友好拢军,LZ是從某網(wǎng)站轉(zhuǎn)戰(zhàn)過來的博主,看到這里很多博主都稱看友們?yōu)閳@友拌滋,LZ斗膽模仿一下朴沿,不過以前,LZ其實都是稱看友們?yōu)樵秤训陌苌啊V癓Z在某網(wǎng)站已經(jīng)寫了一系列文章赌渣,已經(jīng)全部復(fù)制到了園內(nèi)的新博客,主要是設(shè)計模式的內(nèi)容昌犹,各位有興趣的也可以去翻看一下坚芜,其中有不少還是頗受之前猿友們喜愛的。
作為一個程序猿斜姥,修煉的過程就猶如玄幻小說中的主角鸿竖,不僅需要練習(xí)各種武技,內(nèi)氣的修煉的一樣重要铸敏。雖然武技可以迅速的提升主角的實力缚忧,但是在內(nèi)氣太差的情況下,根本發(fā)揮不出武技的十之一二杈笔。
因此闪水,在介紹過設(shè)計模式這一類外功之后,LZ就直接轉(zhuǎn)戰(zhàn)內(nèi)氣修煉蒙具,和各位猿友探討一下JVM的內(nèi)容球榆。
本來這一章應(yīng)該是介紹與GC相關(guān)的內(nèi)容,不過在此之前禁筏,LZ準備先和各位探討一下一個編程的小技巧持钉。當(dāng)然,這個小技巧其實也是與GC密切相關(guān)的篱昔。
不知道各位猿友有沒看過一些JAVA內(nèi)存相關(guān)的文章每强,里面在羅列建議的時候,經(jīng)常會寫出這樣一條建議。
第XX條空执、請在使用完對象之后窘茁,顯示的將對象設(shè)置為null。
原話不一定如此脆烟,但意思是一樣的。話里所描述的意思房待,就是說我們以后寫代碼應(yīng)該這么寫邢羔。
Object obj = new Object();
//to do something
obj = null;
這段代碼有點C/C++的風(fēng)格,obj=null代替了C/C++中的delete obj或者是free(obj)桑孩,相當(dāng)于在提示我們拜鹤,就算有了GC,我們也要像沒有GC一樣流椒,使用完對象就得將其賦為空值敏簿。
首先,LZ這里要說明的是宣虾,將obj賦為空值惯裕,與C/C++中的delete其實有著天壤之別,LZ之所以說它們代替了delete和free绣硝,僅僅是因為它們出現(xiàn)在代碼中的位置類似而已蜻势。
obj=null只做了一件事,就是將obj這個引用變量與new Object()創(chuàng)造的實例的關(guān)聯(lián)斷開鹉胖,實際上握玛,實例所占用的內(nèi)存空間依然沒有釋放。
在提出這個建議的時候甫菠,很多博主或者圖書的作者(因為有不少博主估計也是從書上看的)的本意挠铲,是想盡快消除引用與實例的關(guān)聯(lián),從而誘發(fā)GC在進行垃圾回收時寂诱,將實例所占用的內(nèi)存釋放拂苹。在某些時候,這么做也是為了消除內(nèi)存泄露刹衫。
LZ個人覺得醋寝,許多博主或者是圖書作者,提出這個建議的初衷带迟,主要是針對一些沒有接觸過GC原理以及內(nèi)存管理的程序猿而建議的音羞,因為在不明白相關(guān)知識的前提下,很容易掌握不好變量的作用域仓犬,從而導(dǎo)致不必要的內(nèi)存浪費(個人覺得這個并不是主要目的嗅绰,因為只要沒有發(fā)生內(nèi)存泄露,內(nèi)存終究是要被GC釋放的),甚至是內(nèi)存泄露窘面。
所以為了安全起見翠语,有些高人們就提出了這么一個建議。
有鑒于此财边,LZ個人覺得肌括,在各位掌握了相關(guān)知識之后,完全可以忽略這個建議酣难,而且這么做明顯會降低代碼的清晰度以及增加編碼的負擔(dān)谍夭,然而所換來的好處,卻只是為了避免那根本不一定存在的內(nèi)存泄露憨募。
這里解釋一下為何在有些時候不將對象賦為空值會造成內(nèi)存泄露紧索,我們考慮下面一段代碼。
import java.util.Arrays;
public class Stack {
private static final int INIT_SIZE = 10;
private Object[] datas;
private int size;
public Stack() {
super();
datas = new Object[INIT_SIZE];
}
public void push(Object data){
if (size == datas.length) {
extend();
}
datas[size++] = data;
}
public Object pop(){
if (size == 0) {
throw new IndexOutOfBoundsException("size is zero");
}
return datas[--size];
}
private void extend(){
datas = Arrays.copyOf(datas, 2 * size + 1);
}
}
這段代碼是一個簡單的長度可伸縮的棧實現(xiàn)菜谣,在你寫一段測試代碼去測試它的時候珠漂,會發(fā)現(xiàn)它毫無問題。但是不好意思尾膊,這里面就有一個明顯的“內(nèi)存泄露”媳危,這就是因為一些對象或者說引用沒有設(shè)置為空值所造成的。
如果你還沒看出來眯停,LZ稍微提示一下济舆,各位就會意識到了。這段代碼里面莺债,數(shù)組里的對象只會無限增長滋觉,而不會隨著棧中元素的彈出而減少,減小的僅僅是對外展示的棧的大小size齐邦∽迪溃考慮一個極端的場景,假設(shè)你曾經(jīng)往棧中放入了100萬個對象措拇,最后使用了99萬9千9百9十9個我纪,從外部看來,棧中還只剩一個可用對象了丐吓,但是我們的棧依然持有著100萬個對象的引用浅悉。如果JVM可以活過來的話,想必一定會把你蹂躪到死的券犁。
我們?nèi)鄙俚木褪菍ο筚x為null值的那一步术健,所以pop方法應(yīng)該改為下面的方式,而且各位可以去看一下ArrayList中remove方法的源碼粘衬,也是類似的思路荞估。
public Object pop(){
if (size == 0) {
throw new IndexOutOfBoundsException("size is zero");
}
Object data = datas[--size];
datas[size] = null;
return data;
}
這個內(nèi)存泄露是非常隱蔽的咳促,而且實際使用當(dāng)中不一定就能發(fā)現(xiàn),因為隨著Stack對象的回收勘伺,整個數(shù)組也會被回收跪腹,到時內(nèi)存泄露就被掩蓋了。
所以個人覺得上述的那個建議是完全沒有必要的飞醉,就算遵從了上面的建議冲茸,如果對內(nèi)存管理不熟悉的話,也不會想到上面這個代碼中的問題缅帘。如果想徹底的避免內(nèi)存泄露的發(fā)生噪裕,機械式的主動將對象賦為空值,并不是一個可以從根本上解決問題的辦法股毫。
真正能夠解決問題的辦法,就是掌握好GC的策略與原理召衔,定義一個變量時多注意變量的作用域铃诬,如此才可以更好的避免內(nèi)存泄露。
所以學(xué)好GC原理還是很有必要的苍凛,希望準備走JAVA之路的朋友趣席,尤其是從培訓(xùn)機構(gòu)出來的猿友們(此處沒有鄙視培訓(xùn)機構(gòu)出來的猿友們的意思,因為LZ本人也是半路出家醇蝴,從培訓(xùn)機構(gòu)走上編程之路的程序猿之一)宣肚,一定不要只專注于各個框架的使用以及業(yè)務(wù)的深入,雖然這些事很有必要悠栓,但并不是全部霉涨。
follow me and go the memory world 吧(語法都是浮云)。