天天看點

深入研究java gc

2019/4/2 星期二

深入研究java gc

引出問題和小結!

應該是全網最全的JVM知識點總結 https://www.toutiao.com/i6717184983829578254/

//此連結很重要 推薦收藏

你解釋一下什麼是JVM?什麼是JDK?什麼是JRE?我懵了

https://www.toutiao.com/i6714156440199627276/

小結:

1、為什麼使用CMS gc回收算法?

//答:

因為在CMS gc算法執行的6個步驟中,隻有在第一步(初始标記(STW Initial Mark))和第四步重新标記階段(STW REMARK)才會暫停整個應用,這樣對應用程式所帶來的影響非常小,缺點是産生記憶體碎片過多

2、那CMS GC政策如何導緻記憶體碎片過多?

//答:是因為第二步并發标記(concurrent marking)與回收線程會與應用程式争搶CPU資源,容易産生記憶體碎片,其二:CMS算法在标記清理之後并沒有重新壓縮配置設定存活對象,是以整個老生代會産生很多的記憶體碎片。

3、那為什麼CMS gc政策會耗時比較長呢?

‘stop-the-world’暫停時間也很短暫,耗時較長的(第二步并發标記)标記和(第三步并發預清理)清理都是并發執行的。

記憶體碎片過多如何觸發Full GC?

CMS并不是很完美,它會在兩種場景下産生嚴重的Full GC(Concurrent Failure(并發失敗),Promotion Failure (促銷失敗))

具體見:老年代 CMS gc回收算法 對hbase的影響 https://blog.51cto.com/12445535/2373206

HBase在演進的道路上又如何不斷優化CMS GC?

具體内容見下詳細介紹:

題外話:什麼是java程式的執行流程;java運作時資料區;java的記憶體管理 見如下圖:

java程式執行流程:

深入研究java gc
java運作時資料區:
深入研究java gc
java的記憶體管理:
深入研究java gc

在我們(運作時資料區)之中,記憶體的配置設定一共有五塊:

1、堆記憶體(Heap):儲存真正的程式的資料的部分;

2、&&&棧記憶體(Stack):儲存堆記憶體位址、還儲存基本資料、方法的執行;(所有的資料都在棧記憶體之中)

3、方法區:儲存所有方法的具體的操作,該區域屬于共享;

4、程式計數器:這是一塊很小的記憶體,小到幾乎可以忽略的地步,隻是做一個程式執行順序的記錄,隻是為了标記我們下一步要執行的代碼的順序号;

5、本地方法棧:該棧之中所儲存的都是作業系統的原生函數。

我們關心的主要是堆記憶體、棧記憶體、方法區

在整個的JVM記憶體組成過程之中,(棧記憶體)是一個非常重要的概念,因為在該記憶體之中,他需要儲存的資料是一組内容,

因為所有的方法在進行遞歸調用的時候都會采用棧的模式。觀察遞歸問題中滿棧的原因取決于伺服器記憶體的大小。

記憶體操作有關的兩類異常

stackOverFlowError(棧溢出):如果請求的棧的深度過大,虛拟機可能會抛處。

OutOfMemoryError(記憶體溢出):如果虛拟機的實作中允許、虛拟機棧動态擴充,當記憶體不足以擴充棧的時候,會抛出。【記憶體被沾滿,更多情況下表示堆也配置設定不了了】

實際上上面隻是觀察到了兩類可能出現錯誤的代碼,但是并不是意味着棧中隻能夠儲存一下基本的資訊,實際上棧裡面儲存同樣是一組的資料。

總結:

1、造成stackOverFlowError(棧溢出)OutOfMemoryError(記憶體溢出)的原因是;

2、在JVM棧記憶體中儲存有棧幁的概念,所有的棧記憶體采用先進後出的資料結構來進行我們的存儲。

首先需要了解一下什麼是java的堆内記憶體劃分

在實際情況下:java 堆記憶體劃分分為了(jdk1.8以前和jdk1.8之後)【對于這2者的差別,我們後面介紹】

jvm堆記憶體劃分(jdk1.8以前):

深入研究java gc
jvm堆記憶體劃分(jdk1.8之後):
深入研究java gc

java堆記憶體模型

java的垃圾收集主要指的是java堆記憶體空間,那麼在每一次執行GC的時候需要區分出那些堆記憶體空間需要被回收,那些不應該被回收。 是以為了整個的回收處理友善,JVM将堆記憶體分為如下的幾個組成部分。而這幾個組成部分你還需要去考慮JDK的版本,現在的JVM記憶體劃分就必須考慮JDK1.8以前和JDK1.8之後的問題了。

如果簡化點來了解的話:

1、新生代:那些剛剛建立的對象,剛剛建立的對象有可能會存在有許多垃圾對象,那麼這些對象應該是被優先回收的;

2、老年代:老不死的那類對象,經過了很多次的清理之後你發現該對象依然有用,

3、永久代:intern()方法進行入池的對象實際上就在永久代中,永久代不會被回收。因為其本身屬于一個bug性的存在(也就是jdk崩潰了,死了永久代CIA能消失),是以在jdk1.8之後,将其更換為元空間(就是電腦的直接記憶體)。

舉個例子:我電腦有100G記憶體,80G給了堆記憶體,那剩下的20G就可以給元空間。

在整個記憶體的組成過程之中,每一代的記憶體空間都會有一個伸縮區,那麼該區域就可以由JVM根據空間的使用情況,動态擴充。

當我們适當合理的設定了伸縮區的記憶體大小之後,那麼就可以得到良好的性能提升。也就是說最容易的性能提升就是改變伸縮區的記憶體大小。

首先什麼是java gc 、java對象建立流程

java對象建立流程如圖:

深入研究java gc

1、大多數記憶體對象要麼生存周期比較短,很快就會沒人引用,比如處理RPC請求的buffer可能隻會生存幾微秒;

2、要麼生存周期比較長,比如Block Cache中的熱點Block,可能就會生存幾分鐘,甚至更長時間。

3、基于這樣的事實,JVM将整個堆記憶體分為兩個部分:新生代(young generation)和老生代(tenured generation),除此之外,JVM還有一個非堆記憶體區-Perm區,主要存放class資訊以及其他meta元資訊,

4、其中Young區又分為Eden區和兩個Survivor 區:S0和S1。

5、一個記憶體對象在建立之後,首先會為其在新生代申請一塊記憶體空間,如果這個對象在新生代存活了很長時間,會将其遷移到老生代。

6、在大多數對延遲敏感的業務場景下(比如HBase),建議使用如下JVM參數,-XX:+UseParNewGC和XX:+UseConcMarkSweepGC,其中前者表示對新生代執行并行的垃圾回收機制,而後者表示對老生代執行并行标記-清除垃圾回收機制。

7、可見,JVM允許針對不同記憶體區執行不同的GC政策。

//在 cdh中預設是這樣設定的

-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled

接下來重點先讨論一下年輕代

深入研究java gc
年輕代GC實作複制算法:(年輕代GC政策 – Parallel New Collector)
深入研究java gc

1、對象初始化之後會被放入Young區,更具體的話應該是Eden區,當Eden區滿了之後,會進行一次GC。

2、GC算法會檢查所有對象的引用情況,如果某個對象還有被引用,表示該對象存活。

3、檢查完成之後,會将這些存活的對象移到S0區,并且回收整個Eden區空間,稱為一次Minor GC;

4、接着新對象進來,又會放入Eden區,滿了之後會檢查S0和Eden區存活的對象,将所有存活的對象移到S1區,再回收整個S0和Eden區空間;

5、很容易了解,S0和S1兩個區總會有一個區是預留給下次存放存活對象用的。

這種算法稱為複制算法,對于這種算法,有兩點需要關注:

  1. 算法會執行’stop-the-world’暫停,但時間非常短。因為Young區通常會設定的比較小(一般不建議不超過512M),而且JVM會啟動大量線程并發執行,一次Minor GC一般都會在幾毫秒内完成
  2. 不會産生碎片,每次GC之後都會将存活的對象放入連續的空間(S0或S1)

    記憶體中所有對象都會維護一個計數器,每次Minor GC移動一個對象之後,都會為這個對象的計數器加一。當計數器增加到一定門檻值之後,算法就會認為該對象生命周期很長,會将其移入老生代。該門檻值可以通過JVM參數XX:MaxTenuringThreshold指定。

提高了解篇

年輕代優化算法

深入研究java gc
深入研究java gc
深入研究java gc

年輕代記憶體調整參數(重要):

深入研究java gc

接下來深度研究老年代

什麼是老年代 和老年代的full gc:

深入研究java gc

老生代GC政策 – Concurrent Mark-Sweep(CMS算法)

什麼是CMS 為什麼CMS?

1、每次執行Minor GC之後,都會有部分生命周期較長的對象被移入老生代,一段時間之後,老生代空間也會被占滿。

2、此時就需要針對老生代空間執行GC操作,此處我們介紹Concurrent Mark-Sweep(CMS)算法。

CMS算法整個流程分為6個階段,其中部分階段會執行 ‘stop-the-world’ 暫停,部分階段會和應用線程一起并發執行:

如圖:

深入研究java gc

老年代執行CMS過程:

深入研究java gc

相應的,對于CMS算法,也需要關注兩點:

  1. ‘stop-the-world’暫停時間也很短暫,耗時較長的标記和清理都是并發執行的。
  2. CMS算法在标記清理之後并沒有重新壓縮配置設定存活對象,是以整個老生代會産生很多的記憶體碎片。

提高篇

老年代标記清除算法:

深入研究java gc

老年代标記壓縮算法:

深入研究java gc

老年代記憶體調整參數:

深入研究java gc

永久代調整參數:

深入研究java gc

元空間調整參數:

深入研究java gc

可用gc方式小結:

深入研究java gc

年輕代串行GC(copy)

深入研究java gc

年輕代并行回收GC

深入研究java gc

年輕代并行GC

深入研究java gc

老年代串行GC

深入研究java gc

老年代并行GC

深入研究java gc

常用gc政策:

深入研究java gc

GC調整政策、

深入研究java gc

收集器參數設定

深入研究java gc

G1收集器介紹:

深入研究java gc
深入研究java gc
深入研究java gc
深入研究java gc