垃圾收集機制(Garbage Collection)批判 (轉)

worldblog發表於2007-12-04
垃圾收集機制(Garbage Collection)批判 (轉)[@more@]

垃圾收集機制(Garbage Collection)批判

在版發表這篇文章,似乎有點把矛頭指向Java了。其實不是,GC是所有新一代語言共有的特徵,
, Eiffel,,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++,立刻忍不住火爆脾氣跳將出來,以堅韌不拔的毅力和大無畏精神與對手周旋,
舌戰群儒,哪怕只剩下一個人也要血戰到底。這等奇人當真少見!我真奇怪他整天泡在us上,
不用工作麼?他的老闆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 庫存在大量資源洩漏問題,這一點SUN非常清楚,稱之為s,正在極力修正。但是看來
這裡的問題恐怕不僅是庫編寫者的疏忽,可能根源在於深層的機制,未必能夠輕易解決,搞不好要傷筋動骨。
不過這個問題不是那麼根本,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 slee 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問題。
但是,如果我奶奶跟舒馬赫比賽車輸掉了,你能夠埋怨那輛車子麼?

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-988021/,如需轉載,請註明出處,否則將追究法律責任。

相關文章