來自RedHat的性能和OpenJDK開發(fā)者Aleksey Shipil v,提交了一份新的JEP草案,其內容為創(chuàng)建一個無操作垃圾回收器:一種實際上不進行實際內存回收的GC方式。該回收器旨在幫助JVM實現(xiàn)者和研究者,以及少部分無需垃圾回收的超高性能應用程序。如果這項JEP繼續(xù)推進,新的GC方式將會和現(xiàn)有GC方式一起存在,并且通過顯式激活方式使用。
垃圾回收和Java性能向來都是復雜的話題,為了能夠更清晰的說明,InfoQ聯(lián)系了Java Champions成員、性能專家Martijn Verburg和Kirk Pepperdine。同時,我們也聯(lián)系了無GC Log4j轉型領導者Remko Popma,作為如何實現(xiàn)這一GC方式的代表人物之一。Martijn和Remko確認,在他們看來,無操作GC——既Epsilon GC的首要受益者是GC的開發(fā)者和性能研究人員。Epsilon GC可以作為度量其他垃圾回收器性能的對照組。一個簡單的例子,我們可以將一個應用程序的GC方式設置成無操作GC以減少GC開銷(以避免內存分配和其他不可控因素)。如果相同應用程序在相同情況下運行,只是修改了不同GC算法配置,性能上的不同就可以證明該種GC方式在性能上的開銷。這將會幫助GC開發(fā)者和性能研究人員在更加孤立的方式理解垃圾回收行為。
“我認為這實際上是對精確度量JVM各個部分跨出的一大步。(例如現(xiàn)存JIT的C1/C2編譯器,可能會被轉移到Graal等)。它將會為JVM增加額外的壽命。”——Martijn Verburg
另一方面,一些對性能嚴格要求的應用程序也將會從Epsilon GC受益。有非常少見的應用程序和庫,例如前文提到的Log4j,已經被設計成不產生垃圾,因此他們無需使用垃圾回收器。對于這種類型應用程序,移除垃圾回收器可以提升性能。然而,如Remko強調的,構建一個可以運行在Epsilon GC的庫,“將花費大量的工程努力,以確保應用程序的內存被自己管理,不會耗盡”。但是這樣也必須進行風險和性能評估,以確認選擇無操作GC的收益和實現(xiàn)無垃圾狀態(tài)做的努力是否相同。
應用程序如何可以不產生垃圾看上去很難想象,而且這個話題已經復雜到超出本文討論的范疇,但是如果考慮以下幾個方面可能會容易理解:
JVM將內存分成兩個部分來管理:堆和棧。這就是為什么當缺少內存時會有兩個不同的錯誤(OutOfMemoryError和StackOverflowError)。棧內存只能被當前線程和當前執(zhí)行的方法訪問,因此,當線程離開當前執(zhí)行的方法,這塊內存會被自動釋放,而無需額外垃圾回收器。然而,堆內存可以被整個應用程序在任何時刻訪問,這意味著獨立的垃圾回收器需要區(qū)分內存塊什么時候才不被使用,可以被回收。基本數(shù)據(jù)類型內存總是在棧上分配,因此這對垃圾回收器沒有壓力。如果代碼中基本上都使用基本類型,那么垃圾回收器處理的對象就少了。不產生垃圾不等于不創(chuàng)建對象,如果對象創(chuàng)建滿足以下幾個條件,仍然可以在創(chuàng)建對象之后不需要垃圾回收器:應用程序或者庫在初始化的時候生成有限個數(shù)的對象,然后不斷復用這些對象。但是這需要依賴開發(fā)者非常熟悉應用程序的內存占用。有的時候編譯器可以發(fā)現(xiàn)一些特定對象不會在方法外使用,這被稱為逃逸分析。當確認對象生命周期不會超過方法,其內存可以分配到棧而非堆。因此,這些對象占用的內存會在當前方法結束的時候自動消除。雖然去除垃圾回收可能實現(xiàn),Kirk指出這樣編寫代碼會非常不自然,并且會失去Java提供的很多特性。Martijn也同時指出,內存管理恰恰也是Java能夠在業(yè)界成功的一個主要原因。另外,我們需要牢記,雖然被叫做垃圾回收器,垃圾回收器的任務不僅僅是回收無用的內存,還包括分配新的內存塊,Epsilon GC仍然需要實現(xiàn)該功能。這也正是Kirk的論據(jù),至少理論上,使用Epsilon GC和將其他垃圾回收算法配置成實際不進行垃圾回收之后,在性能上沒有顯著差異。
然而,盡管考慮到所有這些注意事項,Kirk和Martijn證實Epsilon GC還是會對一小部分受眾非常有用,但是根據(jù)Kirk所說,這些受眾將非常少,并懷疑它的實際用處。絕大部分應用程序需要在一些時刻回收內存,因此需要一個有實際功能的垃圾回收器。
“合理的GC停頓時間對于大部分應用程序來說不是問題,因此為什么要為了一個可能存疑的性能優(yōu)勢而放棄Java的所有好處?”——Kirk Pepperdine
Kirk同時提到,新增的每一項新特性都會增加OpenJDK的維護成本,因此OpenJDK開發(fā)人員在添加新特性時必須有全局考慮。Oracle一直在減少垃圾回收器的數(shù)量,以降低維護成本,因此為一小部分用戶添加一個垃圾回收器可能不是一個合理的投資。Aleksey在起草JEP草案的時候,看上去考慮到了這些點,通過JEP草案內容和已經提供的原型來看,初步分析開銷可能不大。事實上,Kirk和Martijn同時指出,考慮到Aleksey的經驗,應該對他所領導的這個倡議保持樂觀。
另一方面,Kirk也強調,雖然OpenJDK是JVM的一個參考實現(xiàn),但是對垃圾回收器沒有強制性要求,這意味著只要完全兼容Java規(guī)范,分發(fā)者可以有他們自己的垃圾回收算法實現(xiàn)。這可能導致公眾意見的分歧:其中一部分人可能認為為特定市場實現(xiàn)的算法可能更適合商業(yè)版本的JVM,另一些人可能會認為這對OpenJDK是一個非常有用的補充。
Remko還建議當性能變得至關重要時,應該考慮使用商業(yè)版JVM,因為在購買許可證上的開銷會比讓工程師選擇和調優(yōu)特定的GC算法要小。但是,即使有些人只選擇使用OpenJDK,Remko和Martijn都提到當前正在開發(fā)中的Shenandoah GC,其目的是針對非常大的堆內存(100GB甚至更大)也只有超低停頓時間。無論如何,專家們的共識是,當應用程序遇到性能問題時,一個經過謹慎選擇的GC算法總是優(yōu)于完全沒有GC。
該提案仍然處于初級階段,在稱為正式JEP之前仍需要進行審查和潤色。讓它稱為正式稿之后,將會被加入到JVM版本中。雖然目前我們只能推測最終會添加到什么版本JVM中,Martijn認為有理由期待Epsilon GC將會加入到Java 10或者11。如果要說有什么好處的話,Epsilon GC至少能夠幫助理解GC的接口,有助于成就一個更加模塊化的JVM。
查看英文原文:The Last Frontier in Java Performance: Remove the Garbage Collector