JVM(8)——GC的影響(總結)

1.基於JVM運行的系統最怕什麼?

基於JVM運行的系統最害怕的問題:**系統卡頓問題!**就是每次一旦年輕代塞滿之後,在進行垃圾回收的時候,這個期間都必須停止系統程序的運行!

2.年輕代gc到底多久一次對系統影響不大?

通常來說是不大的,假如說你的系統運行着,然後每隔幾分鐘或者幾十分鐘執行一次新生代gc,系統卡頓幾十毫秒,就這期間的請求會卡頓幾十毫秒,幾乎用戶都是無感知的,所以新生代gc一般基本對系統性能影響不大。

3.什麼時候新生代gc對系統影響很大?

當你的系統部署在大內存機器上的時候,比如說你的機器是32核64G的機器,此時你分配給系統的內存有幾十個G,新生代的Eden區可能30G~40G的內存。執行垃圾回收大概需要幾秒鐘,此時你發現,可能每過一分鐘,你的系統就要卡頓幾秒鐘,有的請求一旦卡死幾秒鐘就會超時報錯,此時可能會導致你的系統頻繁出錯。

4.如何解決大內存機器的新生代GC過慢的問題?

用G1垃圾回收器,G1基於他的Region內存劃分原理,就可以在運行一段時間之後,比如就針對2G內存的Region進行垃圾回收,此時就僅僅停頓20ms,然後回收掉2G的內存空間,騰出來了部分內存,接着還可以繼續讓系統運行。G1天生就適合這種大內存機器的JVM運行,可以完美解決大內存垃圾回收時間過長的問題。

5.要命的頻繁老年代gc問題

對象進入老年代的幾個條件

  1. 對象年齡太大了,這種對象一般很少,都是系統中確實需要長期存在的核心組件,他們一般不需要被回收掉,所以在新生代熬過默認15次垃圾回收之後就會進入老年代。
  2. 動態年齡判定規則,如果一次新生代gc過後,發現Survivor區域中的幾個年齡的對象加起來超過了Survivor區域的50%,比如說年齡1+年齡2+年齡3的對象大小總和,超過了Survivor區域的50%,此時就會把年齡3以上的對象都放入老年代。
  3. 新生代垃圾回收過後,存活對象太多了,無法放入 Surviovr中,此時直接進入老年代。

第二個和第三個都是很關鍵的,通常如果你的新生代中的Survivor區域內存過小,就會導致上述第二個和第三個條件頻繁發生,然後導致大量對象快速進入老年代,進而頻繁觸發老年代的gc

老年代gc通常來說都很耗費時間,無論是CMS垃圾回收器還是G1垃圾回收器,因爲比如說CMS就要經歷初始標記、併發標記、重新標記、併發清理、碎片整理幾個環節,過程非常的複雜,G1同樣也是如此。

6.JVM性能優化到底在優化什麼?

說白了,系統真正最大的問題,就是因爲內存分配、參數設置不合理,導致你的對象頻繁的進入老年代,然後頻繁觸發老年代gc,導致系統頻繁的每隔幾分鐘就要卡死幾秒鐘。

7.gc概念

  1. Minor GC / Young GC,“新生代”也可以稱之爲“年輕代”,這兩個名詞是等價的。那麼在年輕代中的Eden內存區域被佔滿之後,實際上就需要觸發年輕代的gc,或者是新生代的gc。

  2. Old GC. 一直是說老年代一旦被佔滿之後,就會觸發老年代的gc

  3. **Full GC ** Full GC指的是針對新生代、老年代、永久代的全體內存空間的垃圾回收,所以稱之爲Full GC。“Full”就是整體的意思,所以就是對JVM進行一次整體的垃圾回收,把各個內存區域的垃圾都回收掉。

  4. **Major GC ** 這個其實一般用的比較少,他也是一個非常容易混淆的概念

    有些人把Major GC跟Old GC等價起來,認爲他就是針對老年代的GC,也有人把Major GC和Full GC等價起來,認爲他是針對JVM全體內存區域的GC。

    所以針對這個容易混淆的概念,建議大家以後少提。如果聽到有人說這個Major GC的概念,大家可以問清楚,他到底是想說Old GC呢?還是Full GC呢?

  5. Mixed GC 是G1中特有的概念,其實說白了,主要就是說在G1中,一旦老年代佔據堆內存的45%了,就要觸發Mixed GC,此時對年輕代和老年代都會進行回收

8.Young GC的觸發時機

Young GC其實一般就是在新生代的Eden區域滿了之後就會觸發,採用複製算法來回收新生代的垃圾,

9.Old GC和Full GC的觸發時機

  1. 發生Young GC之前進行檢查,如果“老年代可用的連續內存空間” < “新生代歷次Young GC後升入老年代的對象總和的平均大小”,說明本次Young GC後可能升入老年代的對象大小,可能超過了老年代當前可用內存空間,此時必須先觸發一次Old GC給老年代騰出更多的空間,然後再執行Young GC
  2. 執行Young GC之後有一批對象需要放入老年代,此時老年代就是沒有足夠的內存空間存放這些對象了,此時必須立即觸發一次Old GC
  3. 老年代內存使用率超過了92%,也要直接觸發Old GC,當然這個比例是可以通過參數調整的

一般Old GC很可能就是在Young GC之前觸發或者在Young GC之後觸發的,所以自然Old GC一般都會跟一次Young GC連帶關聯在一起了。

另外一個,在很多JVM的實現機制裏,其實在上述幾種條件達到的時候,他觸發的實際上就是Full GC,這個Full GC會包含Young GC、Old GC和永久代的GC

也就是說觸發Full GC的時候,可能就會去回收年輕代、老年代和永久代三個區域的垃圾對象。

10.永久代滿了之後怎麼辦?

假如存放類信息、常量池的永久代滿了之後,就會觸發一次Full GC。Full GC有上述幾個觸發條件,同時觸發Full GC的時候其實會帶上針對新生代的Young GC,也會有針對老年代的Full GC,還會有針對永久代的GC。所以假如存放類信息、常量池的永久代滿了之後,就會觸發一次Full GC。

這樣Full GC執行的時候,就會順帶把永久代中的垃圾給回收了,但是永久代中的垃圾一般是很少的,因爲裏面存放的都是一些類,還有常量池之類的東西,這些東西通常來說是不需要回收的。如果永久代真的放滿了,回收之後發現沒騰出來更多的地方,此時只能拋出內存不夠的異常了

11Metaspace區域導致的full GC

頻繁Full GC不光是老年代觸發的,有時候也會因爲Metaspace區域的類太多而觸發。

在JVM啓動參數中加入如下兩個參數了:“-XX:TraceClassLoading -XX:TraceClassUnloading”,就是追蹤類加載和類卸載的情況,他會通過日誌打印出來JVM中加載了哪些類,卸載了哪些類。

12定位系統的大對象

jmap工具,通過後臺jstat工具觀察系統,什麼時候發現老年代裏突然進入了幾百MB的大對象,就立馬用jmap工具導出一份dump內存快照。

13內存分析工具

  1. 對線上系統導出一份內存快照
    jmap -dump:format=b,file=文件名 [服務進程ID]
  2. 拿到了內存快照之後,其實就是一份文件,接着就可以用jhat、MAT之類的工具來分析內存了

14內存泄漏

系統創建了大量的對象佔用了內存,其實很多對象是不需要使用的,而且還無法回收掉。

15JVM和GC的運行原理

JVM的內存區域劃分,最核心的就是這麼幾塊了:年輕代、老年代、Metaspace(也就是以前的永久代)。其中年輕代又分成了Eden和2個Survivor,默認比例是8:1:1,隨着Eden區不斷的創建對象,就會逐步的塞滿,當然這個時候可能塞滿Eden區的對象裏大多數都是垃圾對象。一旦Eden區塞滿之後,就會觸發一次Young GC。Young GC會採用複製算法,從GC Roots(方法的局部變量、類的靜態變量)開始追蹤,標記出來存活的對象。然後把存活對象都放入第一個Survivor區域中,也就是S0區域,接着垃圾回收器就會直接回收掉Eden區裏剩餘的全部垃圾對象,在整個這個垃圾回收的過程中全程會進入Stop the Wold狀態,也就是暫停系統工作線程,系統代碼全部停止運行,不允許創建新的對象,一旦垃圾回收全部完畢之後,也就是存活對象都進入了Survivor區域,然後Eden區都清空了,那麼Young GC執行完畢,此時系統恢復工作,繼續在Eden區裏創建對象,下一次如果Eden區滿了,就會再次觸發Young GC,把Eden區和S0區裏的存活對象轉移到S1區裏去,然後直接清空掉Eden區和S0區中的垃圾對象,負責Young GC的垃圾回收器有很多種,但是常用的就是ParNew垃圾回收器,他的核心執行原理就如上所述,只不過他運行的時候是基於多線程併發執行垃圾回收的,

16對象什麼時候進入老年代?

  1. 一個對象在年輕代裏躲過15次垃圾回收,
  2. 對象太大了,超過了一定的閾值,直接進入老年代,不走年輕代
  3. 一次Young GC過後存活對象太多了,導致Survivor區域放不下了,這批對象會進入老年代
  4. 動態年齡判定規則

17老年代的GC是如何觸發的?

  1. 老年代自身可以設置一個閾值,有一個JVM參數可以控制,一旦老年代內存使用達到這個閾值,就會觸發Full GC,一般建議調節大一些,比如92%
  2. 在執行Young GC之前,如果判斷髮現老年代可用空間小於了歷次Young GC後升入老年代的平均對象大小的話,那麼就會在Young GC之前觸發Full GC,先回收掉老年代一批對象,然後再執行Young GC。
  3. 如果Young GC過後的存活對象太多,Survivor區域放不下,就要放入老年代,要是此時老年代也放不下,就會觸發Full GC,回收老年代一批對象,再把這些年輕代的存活對象放入老年代中

總結起來,其實就是老年代一旦快要搞滿了,空間不夠了,必然要垃圾回收一次。Old GC的速度是很慢的,少則幾百毫秒,多則幾秒。所以一旦Full GC很頻繁,就會導致系統性能很差,因爲頻繁要停止系統工作線程,導致系統看起來一直有卡頓的現象。,而且頻繁Full GC還會導致機器CPU負載過高,導致機器性能下降,處理請求能力降低。

18線上頻繁Full GC的幾種表現

  • 機器CPU負載過高;
  • 頻繁Full GC報警;
  • 系統無法處理請求或者處理過慢

19頻繁Full GC的幾種常見原因

  1. 系統承載高併發請求,或者處理數據量過大,導致Young GC很頻繁,而且每次Young GC過後存活對象太多,內存分配不合理,Survivor區域過小,導致對象頻繁進入老年代,頻繁觸發Full GC
  2. 系統一次性加載過多數據進內存,搞出來很多大對象,導致頻繁有大對象進入老年代,必然頻繁觸發Full GC
  3. 系統發生了內存泄漏,莫名其妙創建大量的對象,始終無法回收,一直佔用在老年代裏,必然頻繁觸發Full GC
  4. Metaspace(永久代)因爲加載類過多觸發Full GC
  5. 誤調用System.gc()觸發Full GC

如果jstat分析發現Full GC原因是第一種,那麼就合理分配內存,調大Survivor區域即可。

如果jstat分析發現是第二種或第三種原因,也就是老年代一直有大量對象無法回收掉,年輕代升入老年代的對象病不多,那麼就dump出來內存快照,然後用MAT工具進行分析即可

如果jstat分析發現內存使用不多,還頻繁觸發Full GC,必然是第四種和第五種,此時對應的進行優化即可。

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