目錄
垃圾對象的判定
引用計數法
可達性分析(根搜尋法)
引用狀態
垃圾回收演算法
標記-清除演算法
標記-整理演算法
複製演算法
分代收集演算法
首頁 Java java教程 Java中有關虛擬機器垃圾回收機制的圖文詳解

Java中有關虛擬機器垃圾回收機制的圖文詳解

Aug 09, 2017 am 09:31 AM
java 回收 垃圾

在Java虛擬機器中,物件和陣列的記憶體都是在堆中分配的,垃圾收集器主要回收的記憶體就是再堆記憶體中。如果在Java程式運作過程中,動態建立的物件或陣列沒有及時回收,持續積累,最終堆記憶體就會被佔滿,導致OOM。

JVM提供了一種垃圾回收機制,簡稱GC機制。透過GC機制,能夠在運作過程中將堆中的垃圾物件不斷回收,從而確保程式的正常運作。

垃圾對象的判定

我們都知道,所謂「垃圾」對象,就是指我們在程式的運作過程中不再有用的對象,也就是不再存活的對象。那麼怎麼來判斷堆中的物件是「垃圾」、不再存活的物件呢?

引用計數法

每個物件都有一個引用計數的屬性,用來保存該物件被引用的次數。當引用次數為0時,就表示該物件沒有被引用了,也就不會在使用這個物件了,可以判定為垃圾物件。但是,這種方式有一個很大的Bug,就是無法解決物件間相互引用或循環引用的問題:當兩個物件互相引用,他們兩個和其他任何物件也沒有引用關係,它兩個的引用次數都不為0,因此不會被回收,但實際上這兩個物件已經不再有用了。

可達性分析(根搜尋法)

為了避免使用引用數數法所帶來的問題,Java採用了可達性分析法來判斷垃圾物件。

這種方式可以將所有對象的引用關係想像成一棵樹,從樹的根節點GC Root遍歷所有引用的對象,樹的節點就為可達對象,其他沒有處於節點的對象則為不可達對象。

那麼什麼樣的物件可以當作GC的根節點呢?

  • 虛擬機器堆疊(在幀堆疊中的本機變數表)中引用的物件

  • 方法區中靜態屬性所引用的物件

  • 方法區中常數引用的物件

  • 本機方法堆疊中JNI引用的物件

引用狀態

垃圾回收機制,不管採用是引用數數法,或是可達性分析法,都與物件的引用有關,Java中存在四種引用狀態:

  • #強引用- 我們使用的大部分引用實際上都是強引用,這是使用最普遍的引用。如果一個物件具有強烈引用,就表示它處於可達狀態,垃圾回收器絕不會回收它,即使系統記憶體非常緊張,Java虛擬機寧願拋出 OutOfMemoryError 錯誤,使程式異常終止,也不會回收被強引用所引用的物件。因此,強引用是Java記憶體外洩的主要原因之一。

  • 軟引用 - 一個物件只具有軟引用,如果記憶體空間足夠,垃圾回收器就不會回收它,如果記憶體空間不足了,就會回收這些物件的記憶體。只要垃圾回收器沒有回收它,該物件就可以被程式使用。

  • 弱引用 - 一個物件只具有弱引用,那就類似於是可有可無的。弱引用和軟引用很像,但弱引用的引用等級較低。弱引用與軟引用的區別在於:只具有弱引用的物件擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。

  • 虛引用 - 一個物件只持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。虛引用主要用來追蹤物件被垃圾回收的活動,我們平常一般不會使用。

垃圾回收演算法

透過可達性分析演算法能夠判定哪些物件是需要回收的了,那麼回收具體需要怎樣去執行呢?

標記-清除演算法

首先需要標記可以回收的物件內存,然後在對回收的內存進行清除。

標記-清除演算法(回收前)

#標記-清除演算法(回收後)

但是這樣的話,隨著程式的運行,會不斷分配釋放內存,在堆中會產生很多的不連續的空閒內存區,即內存碎片。這樣即使有足夠多的空閒內存,也不一定能分配出足夠大的內存,並且可能會造成頻繁的GC,影響效率,甚至OOM。

標記-整理演算法

和標記-清除演算法不同的是,標記-整理演算法在標記後不直接清理可回收內存,而是將存活對像都移動到一端,然後清除掉可回收內存。

標記-整理演算法(回收前)

#標記-整理演算法(回收後)

這樣做的好處就是不會產生記憶體碎片。

複製演算法

複製演算法需要先將記憶體分成兩塊,先在其中一塊記憶體上分配內存,當這塊記憶體被分配完後,則執行垃圾回收,然後把存活物件全部複製到另一塊記憶體上,第一塊記憶體則全部清空。

複製演算法(回收前)

#複製演算法(回收後)

這種演算法不會產生記憶體碎片,但是相當於只能使用一半的記憶體空間。同時,複製演算法和存活物件的數量有關,如果存活物件的數量多,那麼複製演算法的效率會大大降低。

分代收集演算法

在Java虛擬機器中,物件的生命週期有長有短,大部分物件的生命週期很短,只有少部分的物件才會在記憶體中存留較長時間,因此可以依據物件生命週期的長短將它們放在不同的區域。在採用分代收集演算法的Java虛擬機堆中,一般分為三個區域,用來分別儲存這三類對象:

  • 新生代- 剛創建的對象,在程式碼運行時一般都會持續不斷地創建新的對象,這些新創建的對像有很多是局部變量,很快就會變成垃圾對象。這些物件被放在一塊稱為新生代的記憶體區域。新生代的特徵是垃圾對像多,存活對象少。

  • 老年代 - 有些物件很早就被創建了,經歷了多次GC也沒有被回收,而是一直存活下來。這些物件被放在一塊稱為老年代的區域。老年代的特徵是存活對像多,垃圾對象少。

  • 永久代 - 一些伴隨虛擬機器生命週期永久存在的對象,例如一些靜態對象,常數等。這些物件被放在一塊稱為永久代的區域。永久代的特色是這些物件一般不需要垃圾回收,會在虛擬機器運作過程中一直存活。 (在Java1.7之前,方法區中儲存的是永久代對象,Java1.7方法區的永久代對象移到了堆中,而在Java1.8永久代已經從堆中移除了,這塊內存給了元空間。

  • 對於新生代區域,每次GC都會有很多垃圾物件被回收,只有少量存活。因此採用複製回收演算法,GC時把剩餘很少的存活對象複製過去即可。

在新生代區域中,並不是依照1:1的比例來進行複製回收,而是依照8:1:1的比例分為了Eden、SurvivorA、SurvivorB三個區域。其中Eden意為伊甸園,形容有許多新生物件在裡面創建;Survivor區則是倖存者,也就是經歷GC後仍存活下來的物件。

Eden區對外提供堆記憶體。當Eden區快要滿了,則進行Minor GC(新生代GC),把存活對象放入SurvivorA區,清空Eden區;

  1. Eden區被清空後,繼續對外提供堆記憶體;

  2. 當Eden區再次被填滿,此時對Eden區和SurvivorA區同時進行Minor GC(新生代GC),把存活物件放入SurvivorB區,此時同時清空Eden區和SurvivorA區;

  3. Eden區繼續對外提供堆內存,並重複上述過程,即在Eden 區填滿後,把Eden區和某個Survivor區的存活物件放到另一個Survivor區;

  4. 當某個Survivor區被填滿,且仍有物件未被複製完畢時,或者某些物件在反覆Survive 15次左右時,則把這部分剩餘物件放到老年代區域;當老年區也被填滿時,進行Major GC(老年代GC),對老年代區域進行垃圾回收。

  5. 老年代區域物件一般存活週期較長,每次GC時,存活的物件比較多,因此採用標記-整理演算法,GC時移動少量存活對象,不會產生記憶體碎片。

  6. 觸發GC的類型

Java虛擬機會把每次觸發GC的資訊印出來,可以根據日誌來分析觸發GC的原因。

GC_FOR_MALLOC:表示是在堆上分配物件時記憶體不足觸發的GC。

  • GC_CONCURRENT:當我們應用程式的堆記憶體達到一定量,或者可以理解為快要滿的時候,系統會自動觸發GC操作來釋放記憶體。

  • GC_EXPLICIT:表示是應用程式呼叫System.gc、VMRuntime.gc介面或收到SIGUSR1訊號時觸發的GC。

  • GC_BEFORE_OOM:表示是在準備拋OOM例外之前進行的最後努力而觸發的GC。

  • #

    以上是Java中有關虛擬機器垃圾回收機制的圖文詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    本網站聲明
    本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

    熱AI工具

    Undresser.AI Undress

    Undresser.AI Undress

    人工智慧驅動的應用程序,用於創建逼真的裸體照片

    AI Clothes Remover

    AI Clothes Remover

    用於從照片中去除衣服的線上人工智慧工具。

    Undress AI Tool

    Undress AI Tool

    免費脫衣圖片

    Clothoff.io

    Clothoff.io

    AI脫衣器

    AI Hentai Generator

    AI Hentai Generator

    免費產生 AI 無盡。

    熱門文章

    R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
    3 週前 By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O.最佳圖形設置
    3 週前 By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O.如果您聽不到任何人,如何修復音頻
    3 週前 By 尊渡假赌尊渡假赌尊渡假赌
    WWE 2K25:如何解鎖Myrise中的所有內容
    4 週前 By 尊渡假赌尊渡假赌尊渡假赌

    熱工具

    記事本++7.3.1

    記事本++7.3.1

    好用且免費的程式碼編輯器

    SublimeText3漢化版

    SublimeText3漢化版

    中文版,非常好用

    禪工作室 13.0.1

    禪工作室 13.0.1

    強大的PHP整合開發環境

    Dreamweaver CS6

    Dreamweaver CS6

    視覺化網頁開發工具

    SublimeText3 Mac版

    SublimeText3 Mac版

    神級程式碼編輯軟體(SublimeText3)

    Java 中的完美數 Java 中的完美數 Aug 30, 2024 pm 04:28 PM

    Java 完美數指南。這裡我們討論定義,如何在 Java 中檢查完美數?

    Java 中的隨機數產生器 Java 中的隨機數產生器 Aug 30, 2024 pm 04:27 PM

    Java 隨機數產生器指南。在這裡,我們透過範例討論 Java 中的函數,並透過範例討論兩個不同的生成器。

    Java中的Weka Java中的Weka Aug 30, 2024 pm 04:28 PM

    Java 版 Weka 指南。這裡我們透過範例討論簡介、如何使用 weka java、平台類型和優點。

    Java 中的史密斯數 Java 中的史密斯數 Aug 30, 2024 pm 04:28 PM

    Java 史密斯數指南。這裡我們討論定義,如何在Java中檢查史密斯號?帶有程式碼實現的範例。

    Java Spring 面試題 Java Spring 面試題 Aug 30, 2024 pm 04:29 PM

    在本文中,我們保留了最常被問到的 Java Spring 面試問題及其詳細答案。這樣你就可以順利通過面試。

    突破或從Java 8流返回? 突破或從Java 8流返回? Feb 07, 2025 pm 12:09 PM

    Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

    Java 中的時間戳至今 Java 中的時間戳至今 Aug 30, 2024 pm 04:28 PM

    Java 中的時間戳記到日期指南。這裡我們也結合範例討論了介紹以及如何在java中將時間戳記轉換為日期。

    創造未來:零基礎的 Java 編程 創造未來:零基礎的 Java 編程 Oct 13, 2024 pm 01:32 PM

    Java是熱門程式語言,適合初學者和經驗豐富的開發者學習。本教學從基礎概念出發,逐步深入解說進階主題。安裝Java開發工具包後,可透過建立簡單的「Hello,World!」程式來實踐程式設計。理解程式碼後,使用命令提示字元編譯並執行程序,控制台上將輸出「Hello,World!」。學習Java開啟了程式設計之旅,隨著掌握程度加深,可創建更複雜的應用程式。

    See all articles