不同的Java垃圾回收器的比較
現在已經是2014年了,但是對大多數開發人員而言有兩件事情仍然是個謎——垃圾回收以及異性(碼農又被嘲笑了)。由於我對後者也不是特別瞭解,我想我還是試著說說前者吧,尤其是隨著Java 8的到來,這個領域也發生了許多重大的變化及提升,其中最重要的莫過於持久代(PermGen)的刪除以及一些令人振奮的新的優化(後面會陸續提及這些)。
說起垃圾回收,許多人都瞭解它的概念,也在日常的程式設計中有所應用。儘管如此,仍有許多我們不太瞭解的東西,而這正是痛苦的根源。關於JVM最大的誤解就是認為它只有一個垃圾回收器,而事實上它有四個不同的回收器,每個都各有其長短。JVM並不會自動地選擇某一個,這事還得落在你我的肩上,因為不同的回收器會帶來吞吐量及應用的暫停時間的顯著的差異。
這四種回收演算法的共同之處在於它們都是分代的,也就是說它們將託管的堆分成了好幾個區域,它假設堆中的許多物件的生命週期都很短,可以很快被回收掉。介紹這塊內容的已經很多了,因此這裡我打算直接講一下這幾個不同的演算法,以及它們的長處及短處。
1.序列回收器
序列回收器是最簡單的一個,你都不會考慮使用它,因為它主要是面向單執行緒環境的(比如說32位的或者Windows)以及比較小的堆。這個回收器工作的時候會將所有應用執行緒全部凍結,就這一點而言就使得它完全不可能會被服務端應用所採用。
如何使用它:你可以開啟-XX:+UseSerialGC這個JVM引數來使用它。
2.並行/吞吐量回收器
下一個是並行回收器( Parallel collector)。這是JVM的預設回收器。正如它的名字所說的那樣,它的最大的優點就是它使用多個執行緒來掃描及壓縮堆。它的缺點就是不管執行的是minor GC還是full GC它都會暫停應用執行緒。並行回收器最適合那些可以容許暫停的應用,它試圖減少由回收器所引起的CPU開銷。
3.CMS回收器
並行回收器之後就是CMS回收器了(concurrent-mark-sweep)。這個演算法使用了多個執行緒(concurrent)來掃描堆並標記(mark)那些不再使用的可以回收(sweep)的物件。這個演算法在兩種情況下會進入一個”stop the world”的模式:當進行根物件的初始標記的時候 (老生代中執行緒入口點或靜態變數可達的那些物件)以及當這個演算法在併發執行的時候應用程式改變了堆的狀態使得它不得不回去再次確認自己標記的物件都是正確的。
使用這個回收器最大的問題就是會碰到promotion failure,這是指在回收新生代及年老代時出現了競爭條件的情況。如果回收器需要將年輕的物件提升到年老代中,而這個時候年老代沒有多餘的空間了,它就只能先進行一次STW(Stop The World)的full GC了——這種情況正是CMS所希望避免的。為了確保這種情況不會發生,你要麼就是增加老生代的大小(或者增加整個堆的大小),要麼就是給回收器分配一些後臺執行緒以便與物件分配的速度進行賽跑。
這個演算法的另一個缺點就是和並行回收器相比,它使用的CPU資源會更多,它使用了多個執行緒來執行掃描和回收,這樣才能讓應用持續提供更高階別的吞吐量。對於大多數長期執行的程式而言,應用的暫停對它們是很不利的,這個時候可以考慮使用CMS回收器。儘管如此,這個演算法也不是預設開啟的。你得指定XX:+UseConcMarkSweepGC來啟用它。假設你的堆小於4G,而你又希望分配更多的CPU資源以避免應用暫停,那麼這就是你要選擇的回收器。然而,如果堆大於4G的話,你可能更希望使用最後的這個——G1回收器。
4.G1回收器
G1( Garbage first)回收器在JDK 7update 4中首次引入,它的設計目標是能更好地支援大於4GB的堆。G1回收器將堆分為多個區域,大小從1MB到32MB不等,並使用多個後臺執行緒來掃描它們。G1回收器會優先掃描那些包含垃圾最多的區域,這正是它的名字的由來(Garbage first)。這個回收器可以通過-XX:UseG1GC標記來啟用。
這一策略減少了後臺執行緒還未掃描完無用物件前堆就已經用光的可能性,而那種情況回收器就必須得暫停應用,這就會導致STW回收。G1的另一個好處就是它總是會進行堆的壓縮,而CMS回收器只有在full GC的時候才會幹這事。
過去幾年裡,大堆一直都是一個充滿爭議的領域,很多開發人員從單機器單JVM模型轉向了單機器多JVM的微服務,元件化的架構。這是許多因素所驅動的,包括隔離程式的元件,簡化部署,避免重新載入應用類到記憶體所產生的開銷(Java 8中這點已經得到了改善)。
儘管如此,這麼做最主要還是希望能避免大堆的GC中長時期的”stop the world”的暫停(在一次大的回收中需要花費數秒才能完成)。像Docker這樣的容器技術也加速了這一程式,它們使得你可以很輕鬆地在同一臺物理機上部署多個應用。
Java 8及G1回收器
Java 8 update 20所引入的一個很棒的優化就是G1回收器中的字串去重(String deduplication)。由於字串(包括它們內部的char[]陣列)佔用了大多數的堆空間,這項新的優化旨在使得G1回收器能識別出堆中那些重複出現的字串並將它們指向同一個內部的char[]陣列,以避免同一個字串的多份拷貝,那樣堆的使用效率會變得很低。你可以使用-XX:+UseStringDeduplication這個JVM引數來試一下這個特性。
Java 8及持久代
Java 8中最大的改變就是持久代的移除,它原本是用來給類後設資料,駐留字串,靜態變數來分配空間的。這在以前都是需要開發人員來針對那些會載入大量類的應用來專門進行堆比例的優化及調整。許多年來都是如此,這也正是許多OutOfMemory異常的根源,因此由JVM來接管它真是再好不過了。即便如此,它本身並不會減少開發人員將應用解耦到不同的JVM中的可能性。
每個回收器都有許多不同的開關和選項來進行調優,這可能會增加吞吐量,也可能會減少,這取決於你的應用的具體的行為了。在下一篇文章中我們會深入講解配置這些演算法的關鍵策略。
相關文章
- 不同Java垃圾回收器的比較Java
- JDK 18 GC垃圾回收機制比較JDKGC
- JAVA垃圾回收機制和Python垃圾回收對比與分析JavaPython
- java垃圾回收器的工作機制Java
- Java11改進的垃圾回收器Java
- Java中的垃圾回收Java
- Java的垃圾回收機制Java
- Java垃圾回收Java
- 不同DBMS的SQL比較SQL
- 對比Ruby和Python的垃圾回收(2):代式垃圾回收機制Python
- java面試一日一題:java中的垃圾回收器Java面試
- Java經典垃圾回收器介紹Java
- Java垃圾回收是如何工作的?Java
- Java中不同的併發實現的效能比較Java
- Java 比較器Java
- 垃圾回收(一)【垃圾回收的基礎】
- Java不同壓縮演算法的效能比較Java演算法
- 更快的JVM垃圾回收器:ShenandoahJVMNaN
- Java——GC(垃圾回收)JavaGC
- JVM 垃圾回收演算法和垃圾回收器JVM演算法
- JVM垃圾回收器JVM
- GC垃圾回收器GC
- 不同解決方案的比較
- Java虛擬機器 —— 垃圾回收機制Java虛擬機
- JVM調優之垃圾定位、垃圾回收演算法、垃圾處理器對比JVM演算法
- Java的垃圾回收(Garbage Collection)機制Java
- 對比Ruby和Python的垃圾回收(1)Python
- JVM(五)垃圾回收器的前世今生JVM
- 深入理解Java虛擬機器 --- 垃圾回收器Java虛擬機
- 每日一問:講講 Java 虛擬機器的垃圾回收Java虛擬機
- java垃圾回收機制Java
- Java 垃圾回收機制Java
- 【JVM】垃圾回收器總結(2)——七種垃圾回收器型別JVM型別
- Java虛擬機器詳解(三)------垃圾回收Java虛擬機
- 【深入理解Java虛擬機器】垃圾回收Java虛擬機
- 不同備份方法的特性比較
- java垃圾回收機制整理Java
- java JVM垃圾回收機制JavaJVM