Java記憶體回收是如何工作的?

來源:互聯網
上載者:User

標籤:記憶體回收行程   javagc   記憶體回收機制   java記憶體回收過程   jvm記憶體回收   

目錄
  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 值,從而標記執行個體為可回收。

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 樣本程式
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 GCt1.t = t2; // No Object Is Eligible for GCt2.t = t3; // No Object Is Eligible for GCt3.t = t1; // No Object Is Eligible for GCt1 = 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();// 沒有對象符合GCt1.t = t2; // 沒有對象符合GCt2.t = t3; // 沒有對象符合GCt3.t = t1; // 沒有對象符合GCt1 = 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

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);}}

輸出:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat 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垃圾收集器。

原文連結: javapapers 翻譯: ImportNew.com - 伍翀
譯文連結: http://www.importnew.com/13493.html

Java記憶體回收是如何工作的?

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.