Java GC系列(3):垃圾回收器種類

Java的內存分配與回收所有由JVM垃圾回收進程自動完成。與C語言不一樣,Java開發者不須要本身編寫代碼實現垃圾回收。這是Java深受你們歡迎的衆多特性之一,可以幫助程序員更好地編寫Java程序。html

下面四篇教程是瞭解Java 垃圾回收(GC)的基礎:java

  1. 垃圾回收簡介
  2. 圾回收是如何工做的?
  3. 垃圾回收的類別
  4. 垃圾回收監視和分析

這篇教程是系列第一部分。首先會解釋基本的術語,好比JDK、JVM、JRE和HotSpotVM。接着會介紹JVM結構和Java 堆內存結構。理解這些基礎對於理解後面的垃圾回收知識很重要。程序員

Java關鍵術語

  • JavaAPI:一系列幫助開發者建立Java應用程序的封裝好的庫。
  • Java 開發工具包 (JDK):一系列工具幫助開發者建立Java應用程序。JDK包含工具編譯、運行、打包、分發和監視Java應用程序。
  • Java 虛擬機(JVM):JVM是一個抽象的計算機結構。Java程序根據JVM的特性編寫。JVM針對特定於操做系統而且能夠將Java指令翻譯成底層系統的指令並執行。JVM確保了Java的平臺無關性。
  • Java 運行環境(JRE):JRE包含JVM實現和Java API。

Java HotSpot 虛擬機

每種JVM實現可能採用不一樣的方法實現垃圾回收機制。在收購SUN以前,Oracle使用的是JRockit JVM,收購以後使用HotSpot JVM。目前Oracle擁有兩種JVM實現而且一段時間後兩個JVM實現會合二爲一。算法

HotSpot JVM是目前Oracle SE平臺標準核心組件的一部分。在這篇垃圾回收教程中,咱們將會了解基於HotSpot虛擬機的垃圾回收原則。shell

JVM體系結構

下面圖片總結了JVM的關鍵組件。在JVM體系結構中,與垃圾回收相關的兩個主要組件是堆內存和垃圾回收器。堆內存是內存數據區,用來保存運行時的對象實例。垃圾回收器也會在這裏操做。如今咱們知道這些組件是如何在框架中工做的。編程

Java堆內存

咱們有必要了解堆內存在JVM內存模型的角色。在運行時,Java的實例被存放在堆內存區域。當一個對象再也不被引用時,知足條件就會從堆內存移除。在垃圾回收進程中,這些對象將會從堆內存移除而且內存空間被回收。堆內存如下三個主要區域:安全

  1. 新生代(Young Generation)
    • Eden空間(Eden space,任何實例都經過Eden空間進入運行時內存區域)
    • S0 Survivor空間(S0 Survivor space,存在時間長的實例將會從Eden空間移動到S0 Survivor空間)
    • S1 Survivor空間 (存在時間更長的實例將會從S0 Survivor空間移動到S1 Survivor空間)
  2. 老年代(Old Generation)實例將從S1提高到Tenured(終身代)
  3. 永久代(Permanent Generation)包含類、方法等細節的元信息

永久代空間在Java SE8特性中已經被移除。服務器

 

 

 

目錄

  1. 垃圾回收介紹
  2. 垃圾回收是如何工做的?
  3. 垃圾回收的類別
  4. 垃圾回收監視和分析

本教程是爲了理解基本的Java垃圾回收以及它是如何工做的。這是垃圾回收教程系列的第二部分。但願你已經讀過了第一部分:《Java 垃圾回收介紹》多線程

Java 垃圾回收是一項自動化的過程,用來管理程序所使用的運行時內存。經過這一自動化過程,JVM 解除了程序員在程序中分配和釋放內存資源的開銷。架構

啓動Java垃圾回收

做爲一個自動的過程,程序員不須要在代碼中顯示地啓動垃圾回收過程。System.gc()Runtime.gc()用來請求JVM啓動垃圾回收。

雖然這個請求機制提供給程序員一個啓動 GC 過程的機會,可是啓動由 JVM負責。JVM能夠拒絕這個請求,因此並不保證這些調用都將執行垃圾回收。啓動時機的選擇由JVM決定,而且取決於堆內存中Eden區是否可用。JVM將這個選擇留給了Java規範的實現,不一樣實現具體使用的算法不盡相同。

毋庸置疑,咱們知道垃圾回收過程是不能被強制執行的。我剛剛發現了一個調用System.gc()有意義的場景。經過這篇文章瞭解一下適合調用System.gc() 這種極端狀況。

Java垃圾回收過程

垃圾回收是一種回收無用內存空間並使其對將來實例可用的過程。

Eden 區:當一個實例被建立了,首先會被存儲在堆內存年輕代的 Eden 區中。

注意:若是你不能理解這些詞彙,我建議你閱讀這篇 垃圾回收介紹 ,這篇教程詳細地介紹了內存模型、JVM 架構以及這些術語。

Survivor 區(S0 和 S1):做爲年輕代 GC(Minor GC)週期的一部分,存活的對象(仍然被引用的)從 Eden 區被移動到 Survivor 區的 S0 中。相似的,垃圾回收器會掃描 S0 而後將存活的實例移動到 S1 中。

(譯註:此處不該該是Eden和S0中存活的都移到S1麼,爲何會先移到S0再從S0移到S1?)

死亡的實例(再也不被引用)被標記爲垃圾回收。根據垃圾回收器(有四種經常使用的垃圾回收器,將在下一教程中介紹它們)選擇的不一樣,要麼被標記的實例都會不停地從內存中移除,要麼回收過程會在一個單獨的進程中完成。

老年代: 老年代(Old or tenured generation)是堆內存中的第二塊邏輯區。當垃圾回收器執行 Minor GC 週期時,在 S1 Survivor 區中的存活實例將會被晉升到老年代,而未被引用的對象被標記爲回收。

老年代 GC(Major GC):相對於 Java 垃圾回收過程,老年代是實例生命週期的最後階段。Major GC 掃描老年代的垃圾回收過程。若是實例再也不被引用,那麼它們會被標記爲回收,不然它們會繼續留在老年代中。

內存碎片:一旦實例從堆內存中被刪除,其位置就會變空而且可用於將來實例的分配。這些空出的空間將會使整個內存區域碎片化。爲了實例的快速分配,須要進行碎片整理。基於垃圾回收器的不一樣選擇,回收的內存區域要麼被不停地被整理,要麼在一個單獨的GC進程中完成。

垃圾回收中實例的終結

在釋放一個實例和回收內存空間以前,Java 垃圾回收器會調用實例各自的 finalize() 方法,從而該實例有機會釋放所持有的資源。雖然能夠保證 finalize() 會在回收內存空間以前被調用,可是沒有指定的順序和時間。多個實例間的順序是沒法被預知,甚至可能會並行發生。程序不該該預先調整實例之間的順序並使用 finalize() 方法回收資源。

  • 任何在 finalize過程當中未被捕獲的異常會自動被忽略,而後該實例的 finalize 過程被取消。
  • JVM 規範中並無討論關於弱引用的垃圾回收機制,也沒有很明確的要求。具體的實現都由實現方決定。
  • 垃圾回收是由一個守護線程完成的。

對象何時符合垃圾回收的條件?

  • 全部實例都沒有活動線程訪問。
  • 沒有被其餘任何實例訪問的循環引用實例。

Java 中有不一樣的引用類型。判斷實例是否符合垃圾收集的條件都依賴於它的引用類型。

引用類型 垃圾收集
強引用(Strong Reference) 不符合垃圾收集
軟引用(Soft Reference) 垃圾收集可能會執行,但會做爲最後的選擇
弱引用(Weak Reference) 符合垃圾收集
虛引用(Phantom Reference) 符合垃圾收集

在編譯過程當中做爲一種優化技術,Java 編譯器能選擇給實例賦 null 值,從而標記實例爲可回收。

1
2
3
4
5
6
7
8
9
10
class Animal {
     public static void main(String[] args) {
         Animal lion = new Animal();
         System.out.println( "Main is completed." );
     }
  
     protected void finalize() {
         System.out.println( "Rest in Peace!" );
     }
}

在上面的類中,lion 對象在實例化行後從未被使用過。所以 Java 編譯器做爲一種優化措施能夠直接在實例化行後賦值lion = null。所以,即便在 SOP 輸出以前, finalize 函數也可以打印出 'Rest in Peace!'。咱們不能證實這肯定會發生,由於它依賴JVM的實現方式和運行時使用的內存。然而,咱們還能學習到一點:若是編譯器看到該實例在將來不再會被引用,可以選擇並提前釋放實例空間。

  • 關於對象何時符合垃圾回收有一個更好的例子。實例的全部屬性能被存儲在寄存器中,隨後寄存器將被訪問並讀取內容。無一例外,這些值將被寫回到實例中。雖然這些值在未來能被使用,這個實例仍然能被標記爲符合垃圾回收。這是一個很經典的例子,不是嗎?
  • 當被賦值爲null時,這是很簡單的一個符合垃圾回收的示例。固然,複雜的狀況能夠像上面的幾點。這是由 JVM 實現者所作的選擇。目的是留下儘量小的內存佔用,加快響應速度,提升吞吐量。爲了實現這一目標, JVM 的實現者能夠選擇一個更好的方案或算法在垃圾回收過程當中回收內存空間。
  • 當 finalize() 方法被調用時,JVM 會釋放該線程上的全部同步鎖。

GC Scope 示例程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
Class GCScope {
     GCScope t;
     static int i = 1 ;
  
     public static void main(String args[]) {
         GCScope t1 = new GCScope();
         GCScope t2 = new GCScope();
         GCScope t3 = new GCScope();
  
         // No Object Is Eligible for GC
  
         t1.t = t2; // No Object Is Eligible for GC
         t2.t = t3; // No Object Is Eligible for GC
         t3.t = t1; // No Object Is Eligible for GC
  
         t1 = null ;
         // No Object Is Eligible for GC (t3.t still has a reference to t1)
  
         t2 = null ;
         // No Object Is Eligible for GC (t3.t.t still has a reference to t2)
  
         t3 = null ;
         // All the 3 Object Is Eligible for GC (None of them have a reference.
         // only the variable t of the objects are referring each other in a
         // rounded fashion forming the Island of objects with out any external
         // reference)
     }
  
     protected void finalize() {
         System.out.println( "Garbage collected from object" + i);
         i++;
     }
  
class GCScope {
     GCScope t;
     static int i = 1 ;
  
     public static void main(String args[]) {
         GCScope t1 = new GCScope();
         GCScope t2 = new GCScope();
         GCScope t3 = new GCScope();
  
         // 沒有對象符合GC
         t1.t = t2; // 沒有對象符合GC
         t2.t = t3; // 沒有對象符合GC
         t3.t = t1; // 沒有對象符合GC
  
         t1 = null ;
         // 沒有對象符合GC (t3.t 仍然有一個到 t1 的引用)
  
         t2 = null ;
         // 沒有對象符合GC (t3.t.t 仍然有一個到 t2 的引用)
  
         t3 = null ;
         // 全部三個對象都符合GC (它們中沒有一個擁有引用。
         // 只有各對象的變量 t 還指向了彼此,
         // 造成了一個由對象組成的環形的島,而沒有任何外部的引用。)
     }
  
     protected void finalize() {
         System.out.println( "Garbage collected from object" + i);
         i++;
     }

GC OutOfMemoryError 的示例程序

GC並不保證內存溢出問題的安全性,粗心寫下的代碼會致使 OutOfMemoryError

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.util.LinkedList;
import java.util.List;
  
public class GC {
     public static void main(String[] main) {
         List l = new LinkedList();
         // Enter infinite loop which will add a String to the list: l on each
         // iteration.
         do {
             l.add( new String( "Hello, World" ));
         } while ( true );
     }
}

輸出:

1
2
3
4
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
     at java.util.LinkedList.linkLast(LinkedList.java:142)
     at java.util.LinkedList.add(LinkedList.java:338)
     at com.javapapers.java.GCScope.main(GCScope.java:12)

接下來是垃圾收集系列教程的第三部分,咱們將會看到經常使用的 不一樣 的Java垃圾收集器

 

 

 

目錄
  1. 垃圾回收介紹
  2. 垃圾回收是如何工做的?
  3. 垃圾回收的類別
  4. 垃圾回收監視和分析

在這篇教程中咱們將學習幾種現有的垃圾回收器。在Java中,垃圾回收是一個自動的進程能夠替代程序員進行內存的分配與回收這些複雜的工做。這篇是垃圾回 收教程系列的第三篇,在前面的第2部分咱們看到了在Java中垃圾回收是如何工做的,那是篇有意思的文章,我推薦你去看一下。第一部分介紹了Java的垃 圾回收,主要有JVM體系結構,堆內存模型和一些Java術語。

Java有四種類型的垃圾回收器:

  1. 串行垃圾回收器(Serial Garbage Collector)
  2. 並行垃圾回收器(Parallel Garbage Collector)
  3. 併發標記掃描垃圾回收器(CMS Garbage Collector)
  4. G1垃圾回收器(G1 Garbage Collector)

每種類型都有本身的優點與劣勢。重要的是,咱們編程的時候能夠經過JVM選擇垃圾回收器類型。咱們經過向JVM傳遞參數進行選擇。每種類型在很大程度上有 所不一樣而且能夠爲咱們提供徹底不一樣的應用程序性能。理解每種類型的垃圾回收器而且根據應用程序選擇進行正確的選擇是很是重要的。

一、串行垃圾回收器

串行垃圾回收器經過持有應用程序全部的線程進行工做。它爲單線程環境設計,只使用一個單獨的線程進行垃圾回收,經過凍結全部應用程序線程進行工做,因此可能不適合服務器環境。它最適合的是簡單的命令行程序。

經過JVM參數-XX:+UseSerialGC可使用串行垃圾回收器。

二、並行垃圾回收器

並行垃圾回收器也叫作 throughput collector 。它是JVM的默認垃圾回收器。與串行垃圾回收器不一樣,它使用多線程進行垃圾回收。類似的是,它也會凍結全部的應用程序線程當執行垃圾回收的時候

三、併發標記掃描垃圾回收器

併發標記垃圾回收使用多線程掃描堆內存,標記須要清理的實例而且清理被標記過的實例。併發標記垃圾回收器只會在下面兩種狀況持有應用程序全部線程。

  1. 當標記的引用對象在tenured區域;
  2. 在進行垃圾回收的時候,堆內存的數據被併發的改變。

相比並行垃圾回收器,併發標記掃描垃圾回收器使用更多的CPU來確保程序的吞吐量。若是咱們能夠爲了更好的程序性能分配更多的CPU,那麼併發標記上掃描垃圾回收器是更好的選擇相比並發垃圾回收器。

經過JVM參數 XX:+USeParNewGC 打開併發標記掃描垃圾回收器。

四、G1垃圾回收器

G1垃圾回收器適用於堆內存很大的狀況,他將堆內存分割成不一樣的區域,而且併發的對其進行垃圾回收。G1也能夠在回收內存以後對剩餘的堆內存空間進行壓縮。併發掃描標記垃圾回收器在STW狀況下壓縮內存。G1垃圾回收會優先選擇第一塊垃圾最多的區域

經過JVM參數 –XX:+UseG1GC 使用G1垃圾回收器

Java 8 的新特性

在使用G1垃圾回收器的時候,經過 JVM參數 -XX:+UseStringDeduplication 。 咱們能夠經過刪除重複的字符串,只保留一個char[]來優化堆內存。這個選擇在Java 8 u 20被引入。

咱們給出了所有的四種Java垃圾回收器,須要根據應用場景,硬件性能和吞吐量需求來決定使用哪種。

垃圾回收的JVM配置

下面的JVM關鍵配置都與Java垃圾回收有關。

運行的垃圾回收器類型

配置 描述
-XX:+UseSerialGC 串行垃圾回收器
-XX:+UseParallelGC 並行垃圾回收器
-XX:+UseConcMarkSweepGC 併發標記掃描垃圾回收器
-XX:ParallelCMSThreads= 併發標記掃描垃圾回收器 =爲使用的線程數量
-XX:+UseG1GC G1垃圾回收器

GC的優化配置

配置 描述
-Xms 初始化堆內存大小
-Xmx 堆內存最大值
-Xmn 新生代大小
-XX:PermSize 初始化永久代大小
-XX:MaxPermSize 永久代最大容量

使用JVM GC參數的例子

1
java -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC -jar java-application.jar

在Java垃圾回收教程的下一部分,咱們將會用一個Java程序演示如何監視和分析垃圾回收。

 

 

 

目錄

  1. 垃圾回收介紹
  2. 垃圾回收是如何工做的?
  3. 垃圾回收的類別
  4. 垃圾回收監視和分析

在這個Java GC系列教程中,讓咱們學習用於垃圾回收監視和分析的工具。而後,選用一種工具來監視一個Java示例程序的垃圾回收過程。若是你是一名初學者,你最好仔細閱讀該系列教程。你能夠從這裏(垃圾回收介紹)開始。

Java GC監視和分析工具

下面是一些可用的工具,每一個都有本身的優點和缺點。咱們能夠經過選擇正確的工具並分析,來提高應用程序的性能。這篇教程中,咱們選用Java VisualVM。

  • Java VisualVM
  • Naarad
  • GCViewer
  • IBM Pattern Modeling and Analysis Tool for Java Garbage Collector
  • HPjmeter
  • IBM Monitoring and Diagnostic Tools for Java-Garbage Collection and Memory
  • Visualizer
  • Verbose GC Analyzer

Java VisualVM

Java VisualVM使用是免費的,其須要安裝Java SE SDK。看一下Java JDK的bin文件夾中(路徑:\Java\jdk1.8.0\bin),這裏面有不少javac和java工具,jvisualvm就是其中之一。

Java VisualVM可以被用於:

  •   生成並分析堆的內存轉儲;
  •   在MBeans上觀察並操做;
  •   監視垃圾回收;
  •   內存和CPU性能分析;

一、啓動VisualVM

jvisualvm位於JDK bin文件夾下,直接點擊就能夠。

二、安裝可視化GC插件

咱們須要安裝可視化GC插件,以便在Java GC過程當中有良好的視覺感覺。

三、監視GC

如今,是時候監視垃圾回收進程了,開啓你的Java程序,它將自動被檢測到並顯示到Java VisualVM界面,左側「Application」(應用程序)窗口下,「Local」(本地節點)下,全部本地運行的Java程序都會被列出。

Java VisualVM是一個Java應用程序,所以它也會被列在其中,教程的意圖在於使用VisualVM來監視它本身的GC進程。

雙擊「Local」(本地)下的VisualVM圖標。

如今,程序監控窗口在右側打開,這有許多不一樣關於應用程序性能的相關監視指數的tab頁,目前爲止,咱們最感興趣的是「Visual GC」,點擊它。

上面圖片顯示在Old、Eden、S0和S1上空間利用狀況,下圖顯示了每部分空間的分配和釋放狀況。它按照指定的刷新率保持持續刷新。

上面圖片所展現的是正常運行程序的狀況,當出現內存泄露或者反常的行爲時,它會在圖表中明確的顯示出來。最少咱們能理解他是與對象內存分配和垃圾回收相關的事情。隨後,經過其餘tab頁(像「Threads」)和Thread Dump的幫助,咱們可以減小這個問題。

在「Monitor」tab頁中,咱們可以監控並定時展現全部堆內存使用狀況圖。經過「Perform GC」按鈕能夠啓動垃圾回收進程。

在「Sampler」tab頁中,咱們可以啓動內存和CPU性能分析,它將顯示詳細每一個實例使用的實時報告,它將幫助咱們明確性能問題。

這篇教程是咱們四篇Java垃圾回收系列教程的最後一篇。

http://www.importnew.com/13838.html

相關文章
相關標籤/搜索