在Java版發(fā)表這篇文章,似乎有點(diǎn)把矛頭指向Java了。其實(shí)不是,GC是所有新一代語言共有的特征,
Python, Eiffel,C#,Roby等無一例外地都使用了GC機(jī)制。但既然Java中的GC最為著名,所以天塌
下來自然應(yīng)該抗著。
這篇短文源于comp.lang.java.programmer跟comp.lang.c++上發(fā)生的一場大辯論,支持C++和Java
的兩派不同勢力展開了新世紀(jì)第一場沖突,跟貼發(fā)言超過350,兩派都有名角壓陣。C++陣營的擂主是
Pete Becker,ACM會員,Dinkumware Ltd. 的技術(shù)副總監(jiān)。此君精通C++和Java,開發(fā)過兩種語言的
核心類庫,但是卻對C++狂熱之極,而對于Java頗不以為然。平時談到Java的時候還好,一旦有人膽
敢用Java來批判C++,立刻忍不住火爆脾氣跳將出來,以堅(jiān)韌不拔的毅力和大無畏精神與對手周旋,
舌戰(zhàn)群儒,哪怕只剩下一個人也要血戰(zhàn)到底。這等奇人當(dāng)真少見!我真奇怪他整天泡在usenet上,
不用工作么?他的老板P.J. Plauger如此寬宏大量?Java陣營主角是一個網(wǎng)名Razzi的兄弟,另外有
Sun公司大名鼎鼎的Peter van der Linden助陣,妙語連珠,寸土必爭,加上人多勢眾,一度占據(jù)優(yōu)勢。
C++陣營里大拿雖然很多,但是大多數(shù)沒有Pete那么多閑工夫,例如Greg Comeau,Comeau公司老板,
每次來個只言片語,實(shí)在幫不了Pete多大忙。但是自從C++陣營中冒出一個無名小子,網(wǎng)名Courage(勇氣),
發(fā)動對Java GC機(jī)制的批判,形勢為之一變。C++陣營眼下處于全攻之勢,Java陣營疲于防守,只能
招架說:“你們沒有證據(jù),沒有統(tǒng)計(jì)資料”,形勢很被動。
垃圾收集(GC)不是一直被Java fans用來炫耀,引以為傲的優(yōu)點(diǎn)么?怎么成了弱點(diǎn)了?我大惑不解,定睛
一看,才覺得此中頗有道理。
首先,Java Swing庫存在大量資源泄漏問題,這一點(diǎn)SUN非常清楚,稱之為bugs,正在極力修正。但是看來
這里的問題恐怕不僅是庫編寫者的疏忽,可能根源在于深層的機(jī)制,未必能夠輕易解決,搞不好要傷筋動骨。
不過這個問題不是那么根本,C++陣營覺得如果抓住對方的弱點(diǎn)攻擊,就算是占了上風(fēng)也沒什么說服力。誰
沒有缺點(diǎn)呢?于是反其道而行之,猛烈攻擊Java陣營覺得最得意的東西,Java的GC機(jī)制本身。
首先來想一想,memory leak到底意味著什么。在C++中,new出來的對象沒有delete,這就導(dǎo)致了memory
leak。但是C++早就有了克服這一問題的辦法——smart pointer。通過使用標(biāo)準(zhǔn)庫里設(shè)計(jì)精致的auto_ptr
以及各種STL容器,還有例如boost庫(差不多是個準(zhǔn)標(biāo)準(zhǔn)庫了)中的四個smart pointers,C++程序員只要
花上一個星期的時間學(xué)習(xí)最新的資料,就可以拍著胸脯說:“我寫的程序沒有memory leak!”。
相比之下,Java似乎更優(yōu)秀,因?yàn)閺囊婚_始你就不用考慮什么特殊的機(jī)制,大膽地往前new,自有GC替你
收拾殘局。Java的GC實(shí)際上是JVM中的一個獨(dú)立線程,采用不同的算法策略來收集heap中那些不再有
reference指向的垃圾對象所占用的內(nèi)存。但是,通常情況下,GC線程的優(yōu)先級比較低,只有在當(dāng)前程序
空閑的時候才會被調(diào)度,收集垃圾。當(dāng)然,如果JVM感到內(nèi)存緊張了,JVM會主動調(diào)用GC來收集垃圾,獲取
更多的內(nèi)存。請注意,Java的GC工作的時機(jī)是:1. 當(dāng)前程序不忙,有空閑時間。2. 空閑內(nèi)存不足。
現(xiàn)在我們考慮一種常見的情況,程序在緊張運(yùn)行之中,沒喲空閑時間給GC來運(yùn)行,同時機(jī)器內(nèi)存很大,
JVM也沒有感到內(nèi)存不足,結(jié)果是什么?對了,GC形同虛設(shè),得不到調(diào)用。于是,內(nèi)存被不斷吞噬,而那些
早已經(jīng)用不著的垃圾對象仍在在寶貴的內(nèi)存里睡大覺。例如:
class BadGc {
public void job1() {
String garbage = "I am a garbage, and just sleeping in your precious memory, " +
"how do you think you can deal with me? Daydreaming! HAHA!!!";
....
}
public void job2() {...}
...
...
public void job1000() {...}
public static void main(String[] args) {
bgc = new BadGc();
bgc.job1();
bgc.job2();
...
bgc.job1000();
}
}
運(yùn)行中,雖然garbage對象在離開job1()之后,就再也沒有用了。但是因?yàn)?/span>程序忙,內(nèi)存還夠用,所以GC得
不到調(diào)度,garbage始終不會被回收,直到程序運(yùn)行到bgc.job1000()時還躺在內(nèi)存里嘲笑你。沒轍吧!
好了,我承認(rèn)這段程序很傻。但是你不要以為這只是理論上的假設(shè),恰恰相反,大多數(shù)實(shí)用中的Java程序都有
類似的效應(yīng)。這就是為什么Java程序狂耗內(nèi)存,而且好像給它多少內(nèi)存吃都不夠。你花上大筆的銀子把內(nèi)存
從128升到256,再升到512,結(jié)果是,一旦執(zhí)行復(fù)雜任務(wù),內(nèi)存還是被輕易填滿,而且多出來的這些內(nèi)存只是
用來裝垃圾,GC還是不給面子地千呼萬喚不出來。等到你的內(nèi)存終于心力交瘁,GC才姍姍來遲,收拾殘局。而
且GC工作的方式也很不好評價,一種方法是一旦有機(jī)會回收內(nèi)存,就把所有的垃圾都回收。你可以想象,這要
花很長時間(幾百M(fèi)的垃圾??!),如果你這時侯正在壓下開炮的按鈕,GC卻叫了暫定,好了,你等死吧!另一
種方法,得到機(jī)會之后,回收一些內(nèi)存,讓JVM感到內(nèi)存不那么緊張時就收手。結(jié)果呢,內(nèi)存里始終有大批垃
圾,程序始終在半死不活的蕩著。最后,GC可以每隔一段時間就運(yùn)行一次,每次只回收一部分垃圾,這是現(xiàn)在
大部分JVM的方式,結(jié)果是內(nèi)存也浪費(fèi)了,還動不動暫停幾百毫秒。難??!
反過來看看C++利用smart pointer達(dá)成的效果,一旦某對象不再被引用,系統(tǒng)刻不容緩,立刻回收內(nèi)存。這
通常發(fā)生在關(guān)鍵任務(wù)完成后的清理(cleanup)時期,不會影響關(guān)鍵任務(wù)的實(shí)時性,同時,內(nèi)存里所有的對象
都是有用的,絕對沒有垃圾空占內(nèi)存。怎么樣?傳統(tǒng)、樸素的C++是不是更勝一籌?
據(jù)統(tǒng)計(jì),目前的Java程序運(yùn)行期間占用的內(nèi)存通常為對應(yīng)C++程序的4-20倍。除了其它的原因,上面所說的是一個
非常主要的因素。我們對memory leak如此憤恨,不就是因?yàn)樗鼘?dǎo)致大量的內(nèi)存垃圾得不到清除嗎?如果有了
GC之后,垃圾比以前還來勢洶洶,那么GC又有什么好處呢?
當(dāng)然,C++的smart pointer現(xiàn)在會使用的人不多,所以現(xiàn)在的C++程序普遍存在更嚴(yán)重的memory leak問題。
但是,如果我奶奶跟舒馬赫比賽車輸?shù)袅耍隳軌蚵裨鼓禽v車子么?
http://www.594k.com/java/html/y2007m1/12051/