垃圾收集器 簡述 Java 垃圾回收機制 在 java 中,程式員是不需要顯示的去釋放一個對象的記憶體的,而是由虛擬機自行執行。在 JVM 中,有一個垃圾回收線程,它是低優先順序的,在正常情況下是不會執行的,只有在虛擬機空閑或 者當前堆記憶體不足時,才會觸發執行,掃面那些沒有被任何引用的對象,並將它們添加 ...
垃圾收集器
簡述 Java 垃圾回收機制
在 java 中,程式員是不需要顯示的去釋放一個對象的記憶體的,而是由虛擬機自行執行。在 JVM
中,有一個垃圾回收線程,它是低優先順序的,在正常情況下是不會執行的,只有在虛擬機空閑或
者當前堆記憶體不足時,才會觸發執行,掃面那些沒有被任何引用的對象,並將它們添加到要回收
的集合中,進行回收。
GC 是什麼?為什麼要 GC
-
GC 是垃圾收集的意思(Gabage Collection),記憶體處理是編程人員容易出現問題的地方,
忘記或者錯誤的記憶體
-
回收會導致程式或系統的不穩定甚至崩潰 Java 提供的 GC 功能可以自動監測對象是否超過
作用域從而達到自動
-
回收記憶體的目的, Java語言沒有提供釋放已分配記憶體的顯示操作方法。
垃圾回收的優點和原理。2 種回收機制
Java 語言最顯著的特點就是引入了垃圾回收機制,它使 Java 程式員在編寫程式時不再考慮記憶體
管理的問題。
由於有這個垃圾回收機制, Java 中的對象不再有“作用域”的概念,只有引用的對象才有“作
用域”。
-
垃圾回收機制有效的防止了記憶體泄露,可以有效的使用可使用的記憶體。
-
垃圾回收器通常作為一個單獨的低級別的線程運行,在不可預知的情況下對記憶體堆中已經死
亡的或很長時間沒有用過的對象進行清除和回收。
程式員不能實時的對某個對象或所有對象調用垃圾回收器進行垃圾回收。垃圾回收有分代複製
垃圾回收、標記垃圾回收、增量垃圾回收。
垃圾回收器的基本原理是什麼?
對於 GC 來說,當程式員創建對象時,GC 就開始監控這個對象的地址、大小以及使用情況。
主動通知虛擬機進行垃圾回收的辦法?
通常,GC 採用有向圖的方式記錄和管理堆(heap)中的所有對象。通過這種方式確定哪些對象
是"可達的",哪些對象是"不可達的"。當 GC 確定一些對象為" 不可達"時,GC 就有責任回收這
些記憶體間。
垃圾回收器可以馬上回收記憶體嗎?
可以。程式員可以手動執行 System.gc(),通知 GC 運行,但是 Java 語言規範並不保證 GC 一
定會執行。
我們能保證 GC 執行嗎?
不能,雖然你可以調用 System.gc() 或者 Runtime.gc(),但是沒有辦法保證GC 的執行。
Java 中引用類型有哪些?
-
強引用:發生 gc 的時候不會被回收。
-
軟引用:有用但不是必須的對象,在發生記憶體溢出之前會被回收。
-
弱引用:有用但不是必須的對象,在下一次 GC 時會被回收。
-
虛引用(幽靈引用/幻影引用):無法通過虛引用獲得對象,用PhantomReference 實現虛
引用,虛引用的用途是在 gc 時返回一個通知。
強引用、軟引用、弱引用、虛引用的區別?
思路: 先說一下四種引用的定義,可以結合代碼講一下,也可以擴展談到ThreadLocalMap
里弱引用用處。
-
強引用
我們平時 new 了一個對象就是強引用,例如 Object obj = new Object();即使在記憶體不足
的情況下,JVM 寧願拋出 OutOfMemory 錯誤也不會回收這種對象
-
軟引用
如果一個對象只具有軟引用,則記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不
足了,就會回收這些對象的記憶體。
SoftReference<String> softRef=new SoftReference<String>(str); // 軟引用
-
用處:軟引用在實際中有重要的應用,例如瀏覽器的後退按鈕。按後退時,這個後退時
顯示的網頁內容是重新進行請求還是從緩存中取出呢?這就要看具體的實現策略了。
-
如果一個網頁在瀏覽結束時就進行內容的回收,則按後退查看前面瀏覽過的頁面
時,需要重新構建
-
如果將瀏覽過的網頁存儲到記憶體中會造成記憶體的大量浪費,甚至會造成記憶體溢出.
如下代碼
-
-
Browser prev = new Browser(); // 獲取頁面進行瀏覽
SoftReference sr = new SoftReference(prev); // 瀏覽完畢後置為軟引用
if(sr.get()!=null){
rev = (Browser) sr.get(); // 還沒有被回收器回收,直接獲取
}else{
prev = new Browser(); // 由於記憶體吃緊,所以對軟引用的對象回收了
sr = new SoftReference(prev); // 重新構建
}
-
弱引用
具有弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的記憶體區域的過
程中,一旦發現了只具有弱引用的對象,不管當前記憶體空間足夠與否,都會回收它的記憶體。
String str=new String("abc"); WeakReference<String> abcWeakRef = newWeakReference<String>(str); str=null; 等價於 str = null; System.gc();
-
虛引用
如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收
器回收。虛引用主要用來跟蹤對象被垃圾回收器回收的活動。
怎麼判斷對象是否可以被回收?
垃圾收集器在做垃圾回收的時候,首先需要判定的就是哪些記憶體是需要被回收的,哪些對象是
「存活」的,是不可以被回收的;哪些對象已經「死掉」了,需要被回收。
一般有兩種方法來判斷:
-
引用計數器法:為每個對象創建一個引用計數,有對象引用時計數器 +1,引用被釋放時計
數 -1,當計數器為 0 時就可以被回收。它有一個缺點不能解決迴圈引用的問題;
-
可達性分析演算法:從 GC Roots 開始向下搜索,搜索所走過的路徑稱為引用鏈。當一個對象
到 GC Roots 沒有任何引用鏈相連時,則證明此對象是可以被回收的。
在 Java 中,對象什麼時候可以被垃圾回收
當對象對當前使用這個對象的應用程式變得不可觸及的時候,這個對象就可以被回收了。
垃圾回收不會發生在永久代,如果永久代滿了或者是超過了臨界值,會觸發完全垃圾回收(Full
GC)。如果你仔細查看垃圾收集器的輸出信息,就會發現永久代也是被回收的。這就是為什麼
正確的永久代大小對避免 Full GC 是非常重要的原因.
JVM 運行時堆記憶體如何分代?
Java 堆從 GC 的角度還可以細分為: 新生代(Eden 區、 From Survivor 區和 To Survivor 區)和
老年代。
參考圖 1:
參考圖 2:
-
從圖中可以看出: 堆大小 = 新生代 + 老年代。其中,堆的大小可以通過參數 –Xms、-Xmx 來
指定。
-
預設的,新生代 ( Young ) 與老年代 ( Old ) 的比例的值為 1:2 ( 該值可以通過參數 –
XX:NewRatio 來指定 ),
即:新生代 ( Young ) = 1/3 的堆空間大小。老年代 ( Old ) = 2/3 的堆空間大小。
其中,新生代 ( Young ) 被細分為 Eden 和 兩個 Survivor 區域,這兩個Survivor 區域分別被命
名為 from 和 to,以示區分.
預設的,Eden: from : to = 8 :1 : 1 ( 可以通過參數–XX:SurvivorRatio 來設定 ),即: Eden
= 8/10 的新生代空間大小,from = to = 1/10 的新生代空間大小
JVM 每次只會使用 Eden 和其中的一塊 Survivor 區域來為對象服務,所以無論什麼時候,總是
有一塊 Survivor 區域是空閑著的。
因此,新生代實際可用的記憶體空間為 9/10 ( 即 90% )的新生代空間。
新生代
是用來存放新生的對象。一般占據堆的 1/3 空間。由於頻繁創建對象,所以新生代會頻繁觸發
MinorGC 進行垃圾回收。新生代又分為 Eden 區、ServivorFrom、 ServivorTo 三個區。
Eden 區
Java 新對象的出生地(如果新創建的對象占用記憶體很大,則直接分配到老年代)。當 Eden 區內
存不夠的時候就會觸發 MinorGC,對新生代區進行一次垃圾回收。
Servivor from 區
上一次 GC 的幸存者,作為這一次 GC 的被掃描者。
Servivor to 區
保留了一次 MinorGC 過程中的幸存者。
MinorGC 的過程(複製->清空->互換)
MinorGC 採用複製演算法。
-
eden、 servicorFrom 複製到 ServicorTo,年齡+1首先,把 Eden 和 ServivorFrom 區域
中存活的對象複製到 ServicorTo 區域(如果有對象的年齡以及達到了老年的標準,則賦值
到老年代區),同時把這些對象的年齡+1(如果 ServicorTo 不夠位置了就放到老年區);
-
清空 eden、 servicorFrom然後,清空 Eden 和 ServicorFrom 中的對象;
-
ServicorTo 和 ServicorFrom 互換最後, ServicorTo 和 ServicorFrom 互換,原
ServicorTo 成為下一次 GC 時的 ServicorFrom 區。
老年代
主要存放應用程式中生命周期長的記憶體對象。
老年代的對象比較穩定,所以 MajorGC (常常稱之為 FULL GC)不會頻繁執行。在進行 FULL
GC 前一般都先進行了一次 MinorGC,使得有新生代的對象晉身入老年代,導致空間不夠用時才
觸發。當無法找到足夠大的連續空間分配給新創建的較大對象時也會提前觸發一次 MajorGC 進
行垃圾回收騰出空間。
FULL GC 採用標記清除演算法:首先掃描一次所有老年代,標記出存活的對象,然後回收沒有標記
的對象。ajorGC 的耗時比較長,因為要掃描再回收。FULLGC 會產生記憶體碎片,為了減少記憶體
損耗,我們一般需要進行合併或者標記出來方便下次直接分配。當老年代也滿了裝不下的時候,
就會拋出 OOM(Outof Memory)異常.
永久代
指記憶體的永久保存區域,主要存放 Class 和 Meta(元數據)的信息,Class 在被載入的時候被放
入永久區域, 它和和存放實例的區域不同,GC 不會在主程式運行期對永久區域進行清理。所以這
也導致了永久代的區域會隨著載入的Class 的增多而脹滿,最終拋出 OOM 異常。
JVM 記憶體為什麼要分成新生代,老年代,持久代。新生代中為什麼要分為 Eden和 Survivor。
思路: 先講一下 JAVA 堆,新生代的劃分,再談談它們之間的轉化,相互之間一些參數的配置
(如: –XX:NewRatio,–XX:SurvivorRatio 等),再解釋為什麼要這樣劃分,最好加一點自己
的理解。
答:
這樣劃分的目的是為了使 JVM 能夠更好的管理堆記憶體中的對象,包括記憶體的分配以及回收。
-
共用記憶體區劃分
- 共用記憶體區 = 持久帶 + 堆 持久帶 = 方法區 + 其他 Java 堆 = 老年代 + 新生代 新生代 = Eden + S0 + S1
-
一些參數的配置
預設的,新生代 ( Young ) 與老年代 ( Old ) 的比例的值為 1:2 ,可以通過參數 –XX:NewRatio 配置。 預設的,Eden : from : to = 8 : 1 : 1 ( 可以通過參數 –XX:SurvivorRatio 來設定) Survivor 區中的對象被覆制次數為 15(對應虛擬機參數-XX:+MaxTenuringThreshold)
-
為什麼要分為 Eden 和 Survivor?為什麼要設置兩個 Survivor 區?
1、如果沒有 Survivor,Eden 區每進行一次 Minor GC,存活的對象就會被送到老年代。 老年代很快被填滿,觸發 Major GC.老年代的記憶體空間遠大於新生代,進行一次 Full GC 消耗的時間比 Minor GC 長得多,所以需要分為 Eden和 Survivor。 2、Survivor 的存在意義,就是減少被送到老年代的對象,進而減少 Full GC 的 發生,Survivor 的預篩選保證,只有經歷 16 次 Minor GC 還能在新生代中存 活的對象,才會被送到老年代。 3、設置兩個 Survivor 區最大的好處就是解決了碎片化,剛剛新建的對象在 Eden 中,經歷一次 Minor GC,Eden 中的存活對象就會被移動到第一塊 survivor space S0,Eden 被清空;等 Eden 區再滿了,就再觸發一次 Minor GC,Eden 和 S0 中的存活對象又會被覆制送入第二塊 survivor space S1(這 個過程非常重要,因為這種複製演算法保證了 S1 中來自 S0 和 Eden 兩部分的 存活對象占用連續的記憶體空間,避免了碎片化的發生)
JVM 中一次完整的 GC 流程是怎樣的,對象如何晉升到老年代
思路:先描述一下 Java 堆記憶體劃分,再解釋 Minor GC,Major GC,full GC,描述它們之間轉
化流程。
答:
-
Java 堆 = 老年代 + 新生代
-
新生代 = Eden + S0 + S1
-
當 Eden 區的空間滿了, Java 虛擬機會觸發一次 Minor GC,以收集新生代的垃圾,存活
下來的對象,則會轉移到 Survivor 區。
-
大對象(需要大量連續記憶體空間的 Java 對象,如那種很長的字元串)直接進入老年態;
-
如果對象在 Eden 出生,並經過第一次 Minor GC 後仍然存活,並且被Survivor 容納的
話,年齡設為 1,每熬過一次 Minor GC,年齡+1,若年齡超過一定限制(15),則被晉
升到老年態。即長期存活的對象進入老年態。
-
老年代滿了而無法容納更多的對象,Minor GC 之後通常就會進行 Full GC,Full GC 清理
整個記憶體堆 – 包括年輕代和年老代。
-
Major GC 發生在老年代的 GC,清理老年區,經常會伴隨至少一次 MinorGC,比 Minor
GC 慢 10 倍以上。
JVM 中的永久代中會發生垃圾回收嗎
垃圾回收不會發生在永久代,如果永久代滿了或者是超過了臨界值,會觸發完全垃圾回收(Full
GC)。如果你仔細查看垃圾收集器的輸出信息,就會發現永久代也是被回收的。這就是為什麼正
確的永久代大小對避免 Full GC 是非常重要的原因。請參考下 Java8:從永久代到元數據區。
(譯者註:Java8 中已經移除了永久代,新加了一個叫做元數據區的 native 記憶體區)
JAVA8 與元數據
在 Java8 中, 永久代已經被移除,被一個稱為“元數據區”(元空間)的區域所取代。元空間
的本質和永久代類似,元空間與永久代之間最大的區別在於:元空間並不在虛擬機中,而是使用
本地記憶體。因此,預設情況下,元空間的大小僅受本地記憶體限制。 類的元數據放入 native
memory, 字元串池和類的靜態變數放入 java 堆中, 這樣可以載入多少類的元數據就不再由
MaxPermSize 控制, 而由系統的實際可用空間來控制。
如何判斷對象可以被回收?
判斷對象是否存活一般有兩種方式:
-
引用計數:
每個對象有一個引用計數屬性,新增一個引用時計數加 1,引用釋放時計數減1,計數為 0
時可以回收。此方法簡單,無法解決對象相互迴圈引用的問題。
-
可達性分析(Reachability Analysis):
從 GC Roots 開始向下搜索,搜索所走過的路徑稱為引用鏈。當一個對象到GC Roots 沒有
任何引用鏈相連時,則證明此對象是不可用的,不可達對象。
引用計數法
在 Java 中,引用和對象是有關聯的。如果要操作對象則必須用引用進行。因此,很顯然一個簡
單的辦法是通過引用計數來判斷一個對象是否可以回收。簡單說,即一個對象如果沒有任何與之
關聯的引用, 即他們的引用計數都不為0, 則說明對象不太可能再被用到,那麼這個對象就是可
回收對象。
可達性分析
為瞭解決引用計數法的迴圈引用問題, Java 使用了可達性分析的方法。通過一系列的“GC
roots”對象作為起點搜索。如果在“GC roots”和一個對象之間沒有可達路徑,則稱該對象是
不可達的。要註意的是,不可達對象不等價於可回收對象, 不可達對象變為可回收對象至少要經
過兩次標記過程。兩次標記後仍然是可回收對象,則將面臨回收。
Minor GC 與 Full GC 分別在什麼時候發生?
新生代記憶體不夠用時候發生 MGC 也叫 YGC,JVM 記憶體不夠的時候發生 FGC
垃圾收集演算法有哪些類型?
-
GC 最基礎的演算法有三類: 標記 -清除演算法、複製演算法、標記-壓縮演算法,我們常用的垃圾回
收器一般都採用分代收集演算法。
-
標記 -清除演算法,“標記-清除”(Mark-Sweep)演算法,如它的名字一樣,演算法分為“標
記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成後統一回收掉所有
被標記的對象。
-
複製演算法,“複製”(Copying)的收集演算法,它將可用記憶體按容量劃分為大小相等的兩
塊,每次只使用其中的一塊。當這一塊的記憶體用完了,就將還存活著的對象複製到另外一塊
上面,然後再把已使用過的記憶體空間一次清理掉。
-
標記-壓縮演算法,標記過程仍然與“標記-清除”演算法一樣,但後續步驟不是直接對可回收對
象進行清理,而是讓所有存活的對象都向一端移動,然後直接清理掉端邊界以外的記憶體
-
分代收集演算法,“分代收集”(Generational Collection)演算法,把 Java 堆分為新生代和
老年代,這樣就可以根據各個年代的特點採用最適當的收集演算法。
說一下 JVM 有哪些垃圾回收演算法?
-
標記-清除演算法:標記無用對象,然後進行清除回收。缺點:效率不高,無法清除垃圾碎
片。
-
複製演算法:按照容量劃分二個大小相等的記憶體區域,當一塊用完的時候將活著的對象複製到
另一塊上,然後再把已使用的記憶體空間一次清理掉。缺點:記憶體使用率不高,只有原來的一
半。
-
標記-整理演算法:標記無用對象,讓所有存活的對象都向一端移動,然後直接清除掉端邊界
以外的記憶體。
-
分代演算法:根據對象存活周期的不同將記憶體劃分為幾塊,一般是新生代和老年代,新生代基
本採用複製演算法,老年代採用標記整理演算法。
標記-清除演算法
標記無用對象,然後進行清除回收。
標記-清除演算法(Mark-Sweep)是一種常見的基礎垃圾收集演算法,它將垃圾收集分為兩個階
段:
- 標記階段:標記出可以回收的對象。
- 清除階段:回收被標記的對象所占用的空間。
標記-清除演算法之所以是基礎的,是因為後面講到的垃圾收集演算法都是在此演算法的基礎上進
行改進的。
優點:實現簡單,不需要對象進行移動。
缺點:標記、清除過程效率低,產生大量不連續的記憶體碎片,提高了垃圾回收的頻率。
標記-清除演算法的執行的過程如下圖所示:
複製演算法
為瞭解決標記-清除演算法的效率不高的問題,產生了複製演算法。它把記憶體空間劃為兩個相等的區
域,每次只使用其中一個區域。垃圾收集時,遍歷當前使用的區域,把存活對象複製到另外一個
區域中,最後將當前使用的區域的可回收的對象進行回收。
優點:按順序分配記憶體即可,實現簡單、運行高效,不用考慮記憶體碎片。
缺點:可用的記憶體大小縮小為原來的一半,對象存活率高時會頻繁進行複製。
複製演算法的執行過程如下圖所示:
標記-整理演算法
在新生代中可以使用複製演算法,但是在老年代就不能選擇複製演算法了,因為老年代的對象存活率
會較高,這樣會有較多的複製操作,導致效率變低。標記- 清除演算法可以應用在老年代中,但是
它效率不高,在記憶體回收後容易產生大量記憶體碎片。因此就出現了一種標記-整理演算法(Mark-
Compact)演算法,與標記-整理演算法不同的是,在標記可回收的對象後將所有存活的對象壓縮到
記憶體的一端,使他們緊湊的排列在一起,然後對端邊界以外的記憶體進行回收。回收後,已用和未
用的記憶體都各自一邊。
優點:解決了標記-清理演算法存在的記憶體碎片問題。
缺點:仍需要進行局部對象移動,一定程度上降低了效率。
標記-整理演算法的執行過程如下圖所示:
分代收集演算法
分代收集法是目前大部分 JVM 所採用的方法,其核心思想是根據對象存活的不同生命周期將內
存劃分為不同的域,一般情況下將 GC 堆劃分為老生代(Tenured/Old Generation)和新生代
(YoungGeneration)。老生代的特點是每次垃圾回收時只有少量對象需要被回收,新生代的特點
是每次垃圾回收時都有大量垃圾需要被回收,因此可以根據不同區域選擇不同的演算法。
當前商業虛擬機都採用分代收集的垃圾收集演算法。分代收集演算法,顧名思義是根據對象的存活周
期將記憶體劃分為幾塊。一般包括年輕代、老年代 和 永久代,
如圖所示:
當前主流 VM 垃圾收集都採用”分代收集” (Generational Collection)演算法, 這種演算法會根據對
象存活周期的不同將記憶體劃分為幾塊, 如 JVM 中的 新生代、老年代、永久代, 這樣就可以根據
各年代特點分別採用最適當的 GC 演算法。
新生代與複製演算法
每次垃圾收集都能發現大批對象已死, 只有少量存活. 因此選用複製演算法, 只需要付出少量存活對
象的複製成本就可以完成收集。
目前大部分 JVM 的 GC 對於新生代都採取 Copying 演算法,因為新生代中每次垃圾回收都要回收
大部分對象,即要複製的操作比較少,但通常並不是按照 1: 1 來劃分新生代。一般將新生代劃
分為一塊較大的 Eden 空間和兩個較小的 Survivor 空間(From Space, To Space),每次使用
Eden 空間和其中的一塊 Survivor 空間,當進行回收時,將該兩塊空間中還存活的對象複製到另
一塊 Survivor 空間中。
老年代與標記複製演算法
因為老年代對象存活率高、沒有額外空間對它進行分配擔保, 就必須採用“標記—清理”或“標
記—整理” 演算法來進行回收, 不必進行記憶體複製, 且直接騰出空閑記憶體。因而採用
Mark-Compact 演算法。
-
JAVA 虛擬機提到過的處於方法區的永生代(Permanet Generation), 它用來存儲 class
類,常量,方法描述等。對永生代的回收主要包括廢棄常量和無用的類
-
對象的記憶體分配主要在新生代的 Eden Space 和 Survivor Space 的From Space(Survivor
目前存放對象的那一塊),少數情況會直接分配到老生代。
-
當新生代的 Eden Space 和 From Space 空間不足時就會發生一次 GC,進行 GC 後,
EdenSpace 和 From Space 區的存活對象會被挪到 To Space,然後將 Eden Space 和
FromSpace 進行清理。
-
如果 To Space 無法足夠存儲某個對象,則將這個對象存儲到老生代。
-
在進行 GC 後,使用的便是 Eden Space 和 To Space 了,如此反覆迴圈。
-
當對象在 Survivor 區躲過一次 GC 後,其年齡就會+1。 預設情況下年齡到達 15 的對象會
被移到老生代中。
GC 垃圾收集器
Java 堆記憶體被劃分為新生代和年老代兩部分,新生代主要使用複製和標記-清除垃圾回收演算法;
年老代主要使用標記-整理垃圾回收演算法,因此 java 虛擬中針對新生代和年老代分別提供了多種
不同的垃圾收集器, JDK1.6 中 SunHotSpot 虛擬機的垃圾收集器如下:
說一下 JVM 有哪些垃圾回收器?
如果說垃圾收集演算法是記憶體回收的方法論,那麼垃圾收集器就是記憶體回收的具體實現。下圖展示
了 7 種作用於不同分代的收集器,其中用於回收新生代的收集器包括 Serial、PraNew、Parallel
Scavenge,回收老年代的收集器包括Serial Old、Parallel Old、CMS,還有用於回收整個 Java
堆的 G1 收集器。不同收集器之間的連線表示它們可以搭配使用。
-
Serial 收集器(複製演算法): 新生代單線程收集器,標記和清理都是單線程,優點是簡單高效;
-
ParNew 收集器 (複製演算法): 新生代收並行集器,實際上是 Serial 收集器的多線程版本,在
多核 CPU 環境下有著比 Serial 更好的表現;
-
Parallel Scavenge 收集器 (複製演算法): 新生代並行收集器,追求高吞吐量,高效利用
CPU。吞吐量 = 用戶線程時間/(用戶線程時間+GC 線程時間),高吞吐量可以高效率的利用
CPU 時間,儘快完成程式的運算任務,適合後臺應用等對交互相應要求不高的場景;
-
Serial Old 收集器 (標記-整理演算法): 老年代單線程收集器,Serial 收集器的老年代版本;
-
Parallel Old 收集器 (標記-整理演算法): 老年代並行收集器,吞吐量優先,
ParallelScavenge 收集器的老年代版本;
-
CMS(Concurrent Mark Sweep)收集器(標記-清除演算法): 老年代並行收集器,以獲取最
短回收停頓時間為目標的收集器,具有高併發、低停頓的特點,追求最短GC 回收停頓時間。
-
G1(Garbage First)收集器 (標記-整理演算法): Java 堆並行收集器,G1 收集器是JDK1.7 提
供的一個新收集器,G1 收集器基於“標記-整理”演算法實現,也就是說不會產生記憶體碎片。
此外,G1 收集器不同於之前的收集器的一個重要特點是:G1 回收的範圍是整個 Java 堆(包
括新生代,老年代),而前六種收集器回收的範圍僅限於新生代或老年代。
Serial 與 Parallel GC 之間的不同之處?
Serial 與 Parallel 在 GC 執行的時候都會引起 stop-the-world。它們之間主要不同 serial 收集
器是預設的複製收集器,執行 GC 的時候只有一個線程,而 parallel 收集器使用多個 GC 線程來
執行。