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