深入研究java gc

2019/4/2 星期二
深入研究java gc
引出問題和小結!
小結:
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

深入研究java gc

深入研究java gc

參考鏈接:
http://hbasefly.com/2016/05/21/hbase-gc-1/

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