一篇文章幫你搞定JVM垃圾回收,和面試官扯皮沒問題了!

Java垃圾回收機制

垃圾收集GC(Garbage Collection)是Java語言的核心技術之一, 在Java中,程序員不需要去關心內存動態分配和垃圾回收的問題,這一切都交給了JVM來處理。GC中判定爲垃圾的標準,標記垃圾的算法以及回收垃圾的算法。

引用計數法

引用計數法就是給對象中添加一個引用計數器,每當有一個地方引用它,計數器就加 1;當引用失效,計數器就減 1;任何時候計數器爲 0 的對象就是不可能再被使用的,可以當做垃圾收集。這種方法實現起來很簡單而且優缺點都很明顯。

  • 優點 執行效率高,程序執行受影響較小
  • 缺點 無法檢測出循環引用的情況,導致內存泄露

可達性分析算法

這個算法的基本思想就是通過一系列的稱爲 “GC Roots” 的對象作爲起點,從這些節點開始向下搜索,節點所走過的路徑稱爲引用鏈,當一個對象到 GC Roots 沒有任何引用鏈相連的話,則證明此對象是不可用的。

在這裏插入圖片描述

第一步:標記 - 清理

垃圾回收的第一步是標記。垃圾回收器此時會找出哪些內存在使用中,還有哪些不是。

標記清除算法

該算法分爲“標記”和“清除”兩個階段:標記階段的任務是標記出所有需要被回收的對象,清除階段就是回收被標記的對象所佔用的空間。它是最基礎的收集算法,效率也很高,但是會帶來兩個明顯的問題:

  • 效率問題
  • 空間問題(標記清除後會產生大量不連續的碎片)

在這裏插入圖片描述

第二步:清除 整理

這一步會刪掉標記出的未引用對象。

標記整理算法

爲了解決複製算法的缺陷,充分利用內存空間,提出了標記整理算法。該算法標記階段和標記清除一樣,但是在完成標記之後,它不是直接清理可回收對象,而是將存活對象都向一端移動,然後清理掉端邊界以外的內存。

**整理:**把所有存活對象堆到同一個地方,這樣就沒有內存碎片了。

在這裏插入圖片描述

第三步:複製

將內存按照容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊用完了,就將還活着的對象複製到另一塊上,然後再把使用過的內存空間一次性清理掉

複製算法

爲了解決效率問題,我們開發出了複製算法。它可以將內存分爲大小相同的兩塊,每次使用其中的一塊。當第一塊的內存使用完後,就將還存活的對象複製到另一塊去,然後再把使用的空間一次清理掉。這樣就使每次的內存回收都是對內存區間的一半進行回收。

簡單來說就是該對象分爲對象面以及空閒面,對象在對象面上創建,對象面上存活的對象會被複制到空閒面,接下來就可以清除對象面的內存。

這種算法的優缺點也比較明顯

  • 優點:解決碎片化問題,順序分配內存簡單高效
  • 缺點:只適用於存活率低的場景,如果極端情況下如果對象面上的對象全部存活,就要浪費一半的存儲空間。

在這裏插入圖片描述

第四步:堆和方法區的垃圾回收

1、方法區的垃圾回收

方法區又叫做永久代。永久代的垃圾回收主要有兩部分:廢棄常量和無用的類。

首先是廢棄常量垃圾回收的一般步驟:

第一步:判定一個常量是否是廢棄常量:沒有任何一個地方對這個常量進行引用就表示是廢棄常量。

第二步:垃圾回收

然後是無用的類垃圾回收的一般步驟

第一步:判定一個類是否是“無用的類”:需要滿足下面三個條件

Java堆中不存在該類的任何實例,也就是該類的所有實例都被回收加載該類的ClassLoader已經被回收該類對應的Class對象在任何地方沒有引用了,也不能通過反射訪問該類的方法。第二步:滿足上面三個條件就可以回收了,但不是強制的。

2、Java 堆的垃圾回收:分代回收算法

分代收集算法

當前虛擬機的垃圾收集都採用分代收集算法,這種算法就是根據具體的情況選擇具體的垃圾回收算法。一般將 java 堆分爲新生代老年代,這樣我們就可以根據各個年代的特點選擇合適的垃圾收集算法。

  • 新生代:存活對象少、垃圾多
  • 老年代:存活對象多、垃圾少

比如在新生代中,每次收集都會有大量對象死去,所以可以選擇複製算法,只需要付出少量對象的複製成本就可以完成每次垃圾收集。而老年代的對象存活機率是比較高的,而且沒有額外的空間對它進行分配擔保,所以我們必須選擇“標記-清除”或“標記-整理”算法進行垃圾收集。

(1)新生代-複製 回收機制

對於新生代區域,由於每次 GC 都會有大量新對象死去,只有少量存活。因此採用 複製 回收算法,GC 時把少量的存活對象複製過去即可。但是從上面我們可以看到,新生代也劃分了三個部分比例:Eden:S1:S2=8:1:1。

其中 Eden 意爲伊甸園,形容有很多新生對象在裏面創建;S1和S2中的S表示Survivor,爲倖存者,即經歷 GC 後仍然存活下來的對象。

工作原理如下:

  1. Eden對外提供堆內存。當 Eden區快要滿了,觸發垃圾回收機制,把存活對象放入 Survivor A 區,清空 Eden 區;
  2. Eden區被清空後,繼續對外提供堆內存;
  3. 當 Eden 區再次被填滿,對 Eden區和 Survivor A 區同時進行垃圾回收,把存活對象放入 Survivor B區,同時清空 Eden 區和Survivor A 區;
  4. 當某個 Survivor區被填滿,把多餘對象放到Old 區;
  5. 當 Old 區也被填滿時,進行 下一階段的垃圾回收。

(2)老年代-標記整理 回收機制

老年代的特點是:存活對象多、垃圾少。因此,根據老年代的特點,這裏僅僅通過少量地移動對象就能清理垃圾,而且不存在內存碎片化。也就是標記整理的回收機制。既然是標記整理算法,而且老年代內部也不存在着內存劃分,所以只需要根據標記整理的具體步驟進行垃圾回收就好了。

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章