早在三年前,Red Hat就啟動了Shenandoah項目。Shenandoah是一種新的Java虛擬機GC算法,目標是利用現代多核CPU的優勢,減少大堆內存在GC方面存在的停頓時間。Shenandoah后來被貢獻給了OpenJDK,正式成為OpenJDK的開源項目,也就是JEP 189。
總的來說,大部分垃圾回收器要么使用古老的標記并清除算法,要么使用分代算法,再結合并發和堆壓縮。垃圾回收器多種多樣,但沒有一種回收器能夠滿足所有的需求,它們總要在某些方面做出折衷,并且不可避免地存在停頓時間。而Shenandoah最大的兩個特點是它伸縮性和超低停頓時間。
Shenandoah最初的目標是把GC停頓時間降到10毫秒以下,并且對內存的支持擴展到TB級別。為了降低停頓時間,回收器需要使用更多的線程來并行處理回收任務。而要在降低停頓時間的同時能夠支持更大的堆空間,回收器對CPU的多核處理能力提出了更高的要求。相比于CMS和G1,Shenandoah不僅進行并行的垃圾標記,在壓縮堆空間時也是并行進行的。
Shenandoah把堆空間分為很多區域,例如整個堆空間是1G,如果每個區域是1M,那么就會有1000多個區域。傳統的標記并清除回收器并沒有區域的概念,而拷貝回收器一般也只有兩個或少數幾個區域。通過更細粒度的分區,Shenandoah可以優先對包含更多垃圾的區域進行回收,同時有助于并行回收工作的進行。
Shenandoah是一個標記拷貝回收器,它的回收工作分為兩個階段。第一個階段是標記階段。在這個階段,回收器會對每個區域里的對象進行標記,并計算它們的數量。第二個階段,回收器對源區域的對象進行掃描,并把存活對象拷貝到目標區域,然后源區域的內存就可以被釋放。這兩個階段看似很簡單,但要讓整個過程并行進行,從而降低停頓時間,事情就會變得復雜很多。
到目前為止,Shenandoah已經實現了很多特性,包括運行時、解釋器、C1屏障和C2屏障、對弱引用的支持、對JNI臨界區域的支持、對System.gc()的支持,等等。Shenandoah目前還算穩定,它的平均性能能夠達到G1的90%,有時候會差一些,比如70%,不過有時候會超過G1,比如150%。不過Shenandoah的停頓時間比G1要短很多,不過相比之前定下的目標,還有很大距離。
目前還沒有把Shenandoah用在Java 9里的計劃,不過如果有人對此感興趣,可以自己從源代碼構建特別版本的JDK來體驗Shenandoah。關于更多Shenandoah的細節可以在這里看到。
不過,從Shenandoah的目標來看,它更適合用在大堆上。所以,如果CPU資源有限,內存也不大,比如小于20G,那么就沒有必要使用Shenandoah。