JVM從入門到入土之實戰JVM調優(一)

前言

文本已收錄至個人GitHub倉庫,歡迎Star:github.com/bin39232820…
種一棵樹最好的時間是十年前,其次是如今 git

我知道不少人不玩qq了,可是懷舊一下,歡迎加入六脈神劍Java菜鳥學習羣,羣聊號碼:549684836 鼓勵你們在技術的路上寫博客github

絮叨

前面的章節面試

其實前面的都只是鋪墊,真正的東西來了,是騾子是馬總得遛遛才知道,因此呢,你們一塊兒來看看針對不一樣的業務場景,分析具體的業務來調優緩存

每日上億請求的電商系統JVM調優

背景引入

咱們的背景是電商系統,電商系統其實通常會拆分紅爲不少的子系統獨立部署,好比商品系統,訂單系統,活動系統,數據統計系統,會員系統等。app

這邊就以訂單系統來作例子jvm

每日上億請求,那麼它會有多少活躍用戶 post

通常每一個用戶平均訪問20次,那麼上億大概有500W 日活躍學習

那麼繼續推算 500W 日活 能有多少下單呢?優化

若是是10% 大概會有50W人下單,那麼天天大概是50W訂單spa

若是50W訂單集中在4小時的高峯期內,那麼平均每秒也才幾十個訂單,若是是幾十個訂單其實並不要過多去關注JVM,基本上每秒佔用一些新生代獲得內存,隔好久,新生代纔會滿,而後一次Minor GC 內存就出來了,沒有啥壓力

可是秒殺,大促銷的場景就不這樣了

若是是雙11 基本上幾分鐘就會有幾十萬的訂單,那麼每秒的下單量 多是上千 破萬

抗住大促銷須要幾臺機器

基本上3臺機器能夠了 每一個機器每秒300個下單 部署的是4核8G

從自己來講這個硬件資源是夠了 扛住每秒300個 問題不大

可是問題在於須要對JVM 有限的內存資源合理分配,讓JVM GC次數減小,並且儘可能避免Full GC

大促銷訂單系統的內存使用模型

基本上每秒處理300個下單來估算,其實由於下單涉及的接口請求是比較耗時的,因此呢每秒處理100到300是差很少的

那麼每一個訂單的實體個人字段我按照1kb來算,單單300個訂單就會有300kb的內存開銷

而後算上一系列的其餘業務對象 就是放大10倍到20倍

此外,除了下單外 還有不少跟訂單相關的其餘操做,好比查詢這些 咱們再擴大10倍

那麼每秒就是 300kb * 20 * 10=60MB的內存開銷,這個內存開銷會再1秒後能夠是回收狀態,以下圖

內存如何分配

假設咱們是4核8G的機器,那麼給到JVM 通常是4G,剩下的給操做系統來使用,其中堆內存咱們能夠給到3G,新生代給1.5G,老年代給1.5G

而後每一個線程的Java虛擬機棧是1M,那麼JVM若是有幾百個線程大概是幾百M

而後永生代 給256內存,基本上4G差很少了

此時JVM的示意圖以下

接下來就很明確了 訂單系統每秒大概300個訂單 ,都是佔據新生代60MB的內存,那麼新生代大概是25秒中就會佔滿,以下圖

25秒以後就會要進行Minor GC了,此時由於有-XX:HandlePromotionFailure選項,因此你能夠認爲須要進行的檢查主要是比較 老年代可用空間大小和歷代Minor GC後進入老年代對象的平均大小,剛開始確定這個檢查是能夠經過的
因此Minor GC直接運行,一會兒能夠回收掉99%的新生代對象,由於除了 最近一秒的訂單請求還在處理,大部分的訂單早就處理完畢了,因此此時可能存活的對象也就是100MB

可是問題來了 若是-XX:SuruvivorRatio 參數默認爲8,那麼此時Eden大概佔用1.2GB內存,每一個Surviuvor是150MB,以下圖

而後再次運行20秒,把Eden區暫滿,再次垃圾回收Eden和S1中的對象,存活對象可能仍是100MB左右 會進入S2,以下圖

此時JVM參數以下

新生代垃圾回收優化之一:Survivor空間夠不夠

首先在進行JVM優化的時候,第一個要考慮的問題就是你經過估算,你的新生代的Survivor到底夠不夠

按照上面的邏輯 首先每次新生代垃圾回收是100MB ,有可能會突破150MB,那麼豈不是常常出現Minor GC事後沒法放到Survivor中,那麼豈不是會頻繁進入到老年代?

還有,即便是Minor GC後 的對象少於150MB,可是即便是100MB的對象進入了Survuvor區,由於這是一批相同年齡對象,直接超過了空間的50%,此時也可能會致使對象進入老年代

因此按這個模型來講,Surivor區域的空間明顯是不夠的

其實這裏建議是把新生代調整爲2G 老年代1G,那麼此時Eden爲1.6G,每一個Survivor爲200MB,以下圖

這個時候,Survivor區域變大,就大大下降了新生代GC事後存活對象在Survivor裏放下不下的問題,或者同齡對象超過50%的狀況

這樣就大大下降了 新生代進入老年代的機率

此時JVM的參數以下

其實對於任何一個系統,首先相似上面的內存使用模型預估以及合理的分配內存,儘可能讓每次Minor GC後的對象都留着Survivor裏,不要進入老年代,這個是你首先要優化的地方

新生代對象躲過多少次垃圾回收進入老年代

你們知道 一個對象連續躲過15次垃圾回收會自動的進入老年代

其實按照上面的內存運行模型來講,基本上20秒觸發一次Minor GC ,也就是一個對象再新生代幾分鐘還沒回收,它就會進入老年代

有寫博客說,要把參數設置高一點 20 30 ,其實這種說法是不對的

由於你對這個參數考慮必須結合系統的運行模型來講,若是躲過了15次GC 都幾分鐘,若是一個對象幾分鐘都不回收,確定是系統裏@Service這類註解

因此誰,考慮問題,必定不要人云亦云,要結合運行原理,本身推演和思考,不一樣的業務系統還都是不同的

其實這個參數你也能夠適當的下降它的值,好比說降到5次,也就是一個對象可以躲過5次Minor GC,在新生代超過1分鐘,儘快讓他進入老年代,別再新生代裏面佔着內存了

總之,這個參數必是結合你的系統具體運行模型來考慮的,此時JVM參數以下

多大的對象直接進入老年代?

另一個邏輯就是說,大對象能夠直接進入老年代,由於大對象說明是要長期存活和使用的

好比在JVM裏可能會緩存一些數據,這個通常能夠結合本身系統中到底有沒有建立大對象來決定的

可是通常來講,這個給他設置1MB足夠,由於不多有對象超過1MB的大對象,若是有,多是你提早分配的,大List之類的

此時JVM的參數以下

指定垃圾回收器

同時你們別忘記了要指定垃圾回收器,新生代使用ParNew,老年代使用CMS,以下JVM參數:

總結

其實你們看完這個案例,就能夠直接去設置本身系統的JVM的參數了,看看你新生代的大小,老年代的大小,Eden和Survivor的大小,而後午估算一下你的系統運行模型

  • 每秒佔用多少內存?
  • 多長時間觸發一次Minor GC
  • 通常Minor GC 後還有多少存活對象
  • Survivor能放的下嗎
  • 會不會頻繁由於Survivor放不下致使對象進入老年代?
  • 會不會由於動態年齡判斷規則進入老年代?

結尾

JVM實戰一,這個是最基本的調優,全部的JVM調優都是根據業務估算來的

由於博主也是一個開發萌新 我也是一邊學一邊寫 我有個目標就是一週 二到三篇 但願能堅持個一年吧 但願各位大佬多提意見,讓我多學習,一塊兒進步。

平常求贊

好了各位,以上就是這篇文章的所有內容了,能看到這裏的人呀,都是真粉

創做不易,各位的支持和承認,就是我創做的最大動力,咱們下篇文章見

六脈神劍 | 文 【原創】若是本篇博客有任何錯誤,請批評指教,不勝感激 !

相關文章
相關標籤/搜索