秒懂JVM的垃圾回收機制
前言
閱讀過王子之前JVM文章的小夥伴們,應該已經對JVM的記憶體分佈情況有了一個清晰的認識了,今天我們就接著來聊聊JVM的垃圾回收機制,讓小夥伴們輕鬆理解JVM是怎麼進行垃圾回收的。
複製演算法、Eden區和Survivor區
首先我們就來探索一下對於JVM堆記憶體中的新生代區域,是怎麼進行垃圾回收的。
實際上JVM是把新生代分為三塊區域的:1個Eden區,2個Survivor區。
其中Eden區佔用80%的記憶體空間,每塊Survivor各佔用10%的記憶體空間。比如Eden區有800M,那麼每個Survivor區就有100M。
平時可以使用的區域是Eden區和其中一塊Survivor區,也就是900M的記憶體空間。
剛開始建立物件的時候,物件都是分配在Eden區中的,如果Eden區快滿了,就會觸發垃圾回收 Young GC,使用的就是複製演算法進行垃圾回收,流程如下:
首先會把Eden區中的存活物件一次性轉入其中一塊空著的Survivor區中。
然後清空Eden區,之後新建立的物件就再次被放入了Eden區中了。
如果下次Eden區快滿了,就會再次觸發Young GC,這個時候會把Eden區和存在物件的Survivor區中存活的物件轉移到另一塊空著的Survivor區中,並清空Eden區和之前存在物件的Survivor區。
這就是複製演算法的流程。
一直要保持一個Survivor區是空的以供複製演算法垃圾回收,而這塊區域只佔用整個記憶體的10%,其他90%的記憶體都能被使用,可見記憶體利用率還是相當高的。
什麼時候進入老年代
接下來我們就來看一下什麼時候會進入老年代,這個問題上篇文章輕鬆理解JVM的分代模型中已經簡單的介紹過了,今天會對此展開進行詳細探索。
1.躲過15次GC後進入老年代
在預設的情況下,如果新生代中的某個物件經歷了15次GC後,還是沒有被回收掉,那麼它就會被轉入到老年代中。
這個具體躲過多少次,是可以自己設定的,通過JVM引數“-XX:MaxTenuringThreshold”來設定,預設是15.
2.動態物件年齡判斷
另一種判斷方式也可以進入老年代,是不用等待GC15次的。
它的大致規則是,假如一批物件總大小大於了當前Survivor區域記憶體的大小的50%,那麼大於等於這批物件年齡的物件就會被轉移到老年代。
小夥伴們可能覺得有些沒看明白這句話的意思,沒關係,我們看一下圖
假設Survivor中有兩個物件,它們都經歷過2次GC,年齡是2歲,而且兩個物件加在一起的大小大於50M,也就是超過了Survivor區域記憶體大小的50%,那麼這個時候,Survivor區域中年齡大於等於2歲的物件就要全部轉移到老年代中。
這就是所謂的動態年齡判斷規則。
要注意的是,年齡1+年齡2+年齡n的多個年齡物件大小超過Survivor區的50%,此時會把年齡n以上的物件放入老年代。
3.大物件直接進入老年代
有一個JVM引數"-XX:PretenureSizeThreshold",預設值是0,表示任何情況都先把物件分配給Eden區。
我們可以給他設定一個位元組數1048576位元組,也就是1M。
它的意思就是當要建立的物件大於1M的時候,就會直接把這個物件放入到老年代中,壓根不會經過新生代。
因為大物件在經歷複製演算法進行GC的時候是會降低效能的,所以直接放入老年代就可以了。
4.Young GC後存活的物件太多無法放入Survivor區
還有一種情況,就是Young GC後存活的物件太多,Survivor區放不下了,這個時候就會把這些物件直接轉移到老年代中。
這裡我們就要思考一個問題了,如果老年代也放不下了怎麼辦呢?
老年代空間分配擔保原則
首先,在執行任何一次Young GC之前,JVM都會先檢查一下老年代可用的記憶體空間是否大於新生代所有物件的總大小。
為啥要檢查這個呢?因為在極端情況下,Young GC後,新生代中所有的物件都存活下來了,那就會把所有新生代中的物件放入老年代中。
如果說老年代可用記憶體大於新生代物件總大小,那麼就可以放心的執行Young GC了。
但是如果老年代的可用記憶體小於新生代物件的總大小,這個時候就會看一個引數“-XX:HandlePromotionFailure”是否設定為true了(可以認為jdk7之後,預設設定為true)。
如果設定為true,那麼進入下一步判斷,就是看看老年代可用的記憶體,是否大於之前每次Young GC後進入老年代物件的平均大小。
如果說老年代的可用記憶體小於平均大小,或者說引數沒有設定成true,那麼就會直接觸發“Full GC”,就是對老年代進行垃圾回收,騰出空間後,再進行Young GC。
如果上邊兩種情況判斷成功,沒有執行Full GC,進行了Young GC,有以下幾種可能:
1.如果Young GC後,存活的物件大小小於Survivor區域的大小,那麼直接進入Survivor區域即可。
2.如果Young GC後,存活的物件大小大於Survivor區域的大小,但是小於老年代可用記憶體大小,那就直接進入老年代。
3.很不幸,老年代可用空間也放不下這些存活物件了,那就會發生“Handle Promotion Failure”的情況,觸發Full GC。
如果Full GC後,老年代可用記憶體還是不夠,那麼就會導致OOM記憶體溢位了。
這段內容可能比較繁瑣,結合記憶體模型,多看兩遍相信小夥伴們是可以讀懂的。
老年代的垃圾回收演算法
接下來我們就來介紹一下老年代的垃圾回收演算法,標記整理演算法,理解起來還是比較容易的。
開始時我們的物件是胡亂分佈的,經過垃圾回收後,會標記出哪些是存活物件,哪些是垃圾物件,而後會把這些存活物件在記憶體中進行整理移動,儘量都挪到一邊去靠在一起,然後再把垃圾物件進行清除,這樣做的好處就是避免了垃圾回收後產生大片的記憶體碎片。
但是這一過程其實是比較耗時的,至少要比新生代的垃圾回收演算法慢10倍。
所以如果系統頻繁出現Full GC,會嚴重影響系統性能,出現頻繁卡頓。
所以JVM優化的一大問題就是減少Full GC頻率。
垃圾回收器
新生代和老年代進行垃圾回收的時候是通過不同的垃圾回收器進行回收的。
Seral和Seral Old垃圾回收器:分別用於回收新生代和老年代。
工作原理是單執行緒執行,垃圾回收的時候會停止我們系統的其他執行緒,讓系統卡死不動,然後執行垃圾回收,這個現在基本已經不會使用了
ParNew和CMS垃圾回收器:分別用於回收新生代和老年代。
它們都是多執行緒併發的,效能更好,現在一般是線上生產系統的標配。
G1垃圾回收器:統一收集新生代和老年代,採用了更加優秀的演算法機制。
這裡只是給大家做一下簡單的介紹,更詳細的內容以後文章會單獨解析。
Stop the World
JVM最大的痛點就是Stop the World了。
在垃圾回收的時候,儘可能的要讓垃圾回收器專心的去做垃圾回收的操作(防止垃圾回收的時候還在建立新物件,那不就亂套了嗎),所以此時JVM會在後臺進入Stop the World狀態。
進入這個狀態後,會直接停止我們系統的工作執行緒,讓我們的程式碼不在執行。
接著垃圾回收完成後,會恢復工作執行緒,程式碼就可以繼續運行了。
所以說只要是經歷GC,其實就會讓系統卡死一段時間,新生代的垃圾回收可能感受不到太多,單老年代的垃圾回收耗時更多,可能會明顯的感覺到系統的卡死。
所以說無論是新生代的垃圾回收還是老年代的垃圾回收,我們都應該儘量的減少它們的頻率。
總結
今天的乾貨內容還是比較多的,相信小夥伴們閱讀後對JVM會有一個更深的瞭解。
建議小夥伴們自己找資料瞭解一下幾種垃圾回收器的實現原理,我們之後的文章會陸續介紹。
好了,那就到這裡了,歡迎評論區留言討論。你的支援就是我更新的動力!
往期文章推薦:
大白話談JVM的類載入機制
JVM記憶體模型不再是祕密
輕鬆理解JVM的分代模型
相關推薦
3分鐘搞懂jvm垃圾回收機制
幾句話概括jvm垃圾回收機制 1. jvm在堆記憶體中分為新生代和老年代。 2. 新生代中分為eden區和from區、to區。新來的物件會放到eden區內。當eden區放不下了,會將eden區的物件一道from區。要是from區裝不下,就放到將from區裡的物件移到to區。如果to區記憶體也
JVM垃圾回收機制
root 不一定 引用 不可達 tenured jvm 空間 大量 jvm垃圾回收 在java中是通過引用來和對象進行關聯的,也就是說如果要操作對象,必須通過引用來進行。那麽很顯然一個簡單的辦法就是通過引用計數來判斷一個對象是否可以被回收。不失一般性,如果一個對象沒有任何引
JVM垃圾回收機制演算法分析
JVM記憶體執行時資料區 一、什麼是垃圾回收機制gc垃圾回收機制&&演算法 什麼是垃圾回收機制: 不定時去堆記憶體清理不可達物件。不可達的物件並不會馬上就會直接回收,而是至少要經過兩次標記的過程。 public class Test { public st
JVM 垃圾回收機制與GC效能調優
一、GC概要: JVM堆相關知識 為什麼先說JVM堆? JVM的堆是Java物件的活動空間,程式中的類的物件從中分配空間,其儲存著正在執行著的應用程式用到的所有物件。這些物件的建立方式就是那些new一類的操作,當物件
JVM系列第8講:JVM 垃圾回收機制
在第 6 講中我們說到 Java 虛擬機器的記憶體結構,提到了這部分的規範其實是由《Java 虛擬機器規範》指定的,每個 Java 虛擬機器可能都有不同的實現。其實涉及到 Java 虛擬機器的記憶體,就不得不談到 Java 虛擬機器的垃圾回收機制。因為記憶體總是有限的,我們需要一個機制來不斷地回收廢棄的記憶體
JVM——垃圾回收機制
文章目錄 判斷物件是否存活 引用計數法 可達性分析 垃圾收集演算法 標記—清除 複製演算法 標記—複製演算法 標記—整理演算法 分代收集演算法
Java:JVM垃圾回收機制
JVM垃圾回收機制 提到Java垃圾回收機制就不得不提到一個方法: system.gc() 用於呼叫垃圾收集器,在呼叫時垃圾收集器將執行以回收未使用的記憶體空間,它將嘗試釋放被丟棄物件所佔用的空間。 作為程式設計師有必要了解gc方
JVM垃圾回收機制入門
1. 先看Java的記憶體儲存結構 假設你是一個普通的 Java 物件,你出生在 Eden 區,在 Eden 區有許多和你差不多的小兄弟、小姐妹,可以把 Eden 區當成幼兒園,在這個幼兒園裡大家玩了很長時間。Eden 區不能無休止地放你們在裡面,所以當年紀稍大,你就要被送
(面試題)有關JVM垃圾回收機制的那些演算法
三種垃圾回收演算法 標記-清除(年老代) 標記-整理(即標記-壓縮)(年老代) 複製(年輕代) 1、標記-清除演算法 原理: 從根集合節點進行掃描,標記出所有的存活物件,最後掃描整個記憶體空間並清除沒有標記的物件(即死亡物件)
JVM 垃圾回收機制
引用類型 引用 垃圾收集器 內容 清理 才會 空閑 碎片 刪除 首先JVM的內存結構包括五大區域: 程序計數器、虛擬機棧、本地方法棧、方法區、堆區。其中程序計數器、虛擬機棧和本地方法棧3個區域隨線程啟動與銷毀, 因此這幾個區域的內存分配和回收都具有確定性,不需要過多考慮回收
深入理解JVM學習筆記(十九、JVM 垃圾回收機制---如何判斷物件是否為垃圾【引用計數法】)
一、引用計數法 引用計數演算法作為垃圾收集器最早的演算法,有其優勢,也有其劣勢,雖然現在的JVM都不再採用引用計數演算法進行垃圾回收【例如Sun的Java hotspot採用了火車演算法進行垃圾回收】,但這種演算法也並未被淘汰,在著名的單程序高併發快取Red
[深入理解JVM 七]---Jvm垃圾回收機制
本篇部落格大部分內容來自《深入理解java虛擬機器》,也參考了http://jbutton.iteye.com/blog/1569746這篇文章的部分內容,這裡註明出處。這篇部落格也是這個系列的第二篇,在這篇部落格裡我會對java的記憶體回收機制做個詳細的整理。
JVM垃圾回收機制總結
JVM分為三個主要的子系統 (1)類載入器子系統(2)執行時資料區(3)執行引擎 1.類載入器子系統 Java的動態類載入功能是由類載入器子系統處理。當它在執行時(不是編譯時)首次引用一個類時,它載入、連結並初始化該類檔案。 1.1載入 類由此元件載入,遵循委託
幾種JVM垃圾回收機制
1.1.標記-清除收集器 這種收集器首先遍歷物件圖並標記可到達的物件,然後掃描堆疊以尋找未標記物件並釋放它們的記憶體。這種收集器一般使用單執行緒工作並停止其他操作。 1.2.標記-壓縮收集器 有時也叫標記-清除-壓縮收集器,與標記-清除收集器有相同的標記階段。在第二階段,
JVM垃圾回收機制(一)
一、什麼是垃圾? 1:引用計數演算法:給物件中加一個引用計數器,每當有一個引用指向它時,計數器的值就加一,引用失效時,計數器的值就減一。當該物件引用計數器等於0的時候就被視為垃圾。 該演算法存在很大的缺陷,若兩個物件存在互相引用,則兩者的引用計數器都
深入理解JVM學習筆記(二十二、JVM 垃圾回收機制---如何回收垃圾---回收策略【複製演算法】)
上一節我們講到了標記-清除演算法因為需要進行兩次記憶體掃描導致效率不高,那麼這一節我們介紹一種複製演算法,比較好的解決了這個問題。 講複製演算法前,我們先回顧一下JVM的記憶體結構。JVM記憶體大體分為兩大塊,分別為執行緒共享區、執行緒獨佔區。
JVM記憶體管理、JVM垃圾回收機制、新生代、老年代以及永久代
如果大家想深入的瞭解JVM,可以讀讀周志明《深入理解Java虛擬機器:JVM高階特性與最佳實踐》 需要掌握的東西,包括以下內容、判斷物件存活還是死亡的演算法(引用計數演算法、可達性分析演算法)、常見的垃圾收集演算法(複製演算法、分代收集演算法等以及這
Java虛擬機器的JVM垃圾回收機制
1.JVM記憶體空間 JVM堆(Heap)= 新生代(Young) + 舊生代(Tenured) 分割槽作用: 新建立的物件通常先將其分配在新生代中,在新生代中經過若干次GC之後仍未釋放的物件,再將它移動到舊生代。為了讓記憶體回收更高效(GC會暫停JVM中的應用
深入理解Java虛擬機器——JVM垃圾回收機制和垃圾收集器詳解
一:概述 說起垃圾回收(Garbage Collection,GC),很多人就會自然而然地把它和Java聯絡起來。在Java中,程式設計師不需要去關心記憶體動態分配和垃圾回收的問題,顧名思義,垃圾回收就是釋放垃圾佔用的空間,這一切都交給了JVM來處理。本文主要解答三個
JVM垃圾回收機制之對象回收算法
後臺線程 stat 無需 搜索 容易 語言 可用 需要 jvm內存 前言 在前面的文章中,介紹了JVM內存模型分為:堆區、虛擬機棧、方法區、本地方法區和程序計數器,其中堆區是JVM中最大的一塊內存區域,在Java中的所有對象實例都保存在此區域,它能被所有線程共享。