不知道大家有沒有聽過扁鵲三兄弟的故事,
故事是這樣的:
魏文王問扁鵲:你們三兄弟都精通醫術,誰是醫術最好的呢?
扁鵲回答:大哥最好,二哥次之,我最差,
魏文王不解的問:為什么這樣說呢?
扁鵲答:大哥治病是在病人發作之前,那時候病人自己不覺得有病,但大哥就下藥鏟除了病根,使他的醫術難以被人認可,所以沒有名氣;
二哥治病是在病起之初,癥狀尚不十分明顯,病人也沒有覺得痛苦,二哥就能藥到病除,所以大家的印象就是小病找二哥;
我治病是在病人危急時刻,病人痛苦萬分,家人心急如焚,他們看到我治病時在經脈上扎針穿刺,或以毒試毒,或動大手續使病人減輕痛苦直至痊愈,所以我聞名天下,

為什么要講這個故事呢?
因為JVM調優跟這個故事很像,JVM調優也有這三個階段:
1. 在專案部署到線上之前,基于可能的并發量進行預估調優
2. 在專案運行程序中,部署監控收集性能資料,平時分析日志進行調優
3. 線上出現OOM,進行問題排查與調優
這里每一點展開來講涉及到的知識點都比較多,打算用三篇文章講清楚每一階段的調優,本篇文章講第一階段,主要討論堆區調優,
JVM調優
很多同學可能不太理解:為什么要做JVM調優?
一、防止出現OOM
即在系統部署之前,根據一些關鍵資料進行預估不同記憶體區域需要給多少記憶體合適
二、解決OOM
即線上出現了OOM,應該如何調優以保證程式能正常運行
二、減少full gc出現的頻率
這個主要是堆區,如果設定的不合理就會頻繁full gc,導致系統運行一陣暫停一陣,導致體驗下降
場景
這里以億級流量秒殺電商系統為例:
- 如果每個用戶平均訪問20個商品詳情頁,那訪客數約等于500w(一億 / 20)
- 如果按轉化率10%來算,那日均訂單約等于50w(500w * 10%)
- 如果40%的訂單是在秒殺前兩分鐘完成的,那么每秒產生1200筆訂單(50w * 30% / 120s)
- 訂單支付又涉及到發起支付流程、物流、優惠券、推薦、積分等環節,導致產生大量物件,這里我們假設整個支付流程生成的物件約等于20K,那每秒在Eden區生成的物件約等于20M(1200筆 * 20K)
- 在生產環境中,訂單模塊還涉及到百萬商家查詢訂單、改價、包郵、發貨等其他操作,又會產生大量物件,我們放大10倍,即每秒在Eden區生成的物件約等于200M(其實這里就是在大并發場景下可以考慮服務降級的地方,架構其實就是取舍)
這里的假設資料都是大部分電商系統的通用概率,是有一定代表性的,
如果你作為這個系統的架構師,面對這樣的場景,你會如何做JVM調優呢?即將運行該系統的JVM堆區設定成多大呢?
前置知識
那做JVM調優需要什么的基礎呢?
- 了解Java中基本資料型別的位元組長度
public class TestLength {
public static void main(String[] args) {
System.out.println("Byte.SIZE=" + Byte.SIZE / 8);
System.out.println("Character.SIZE=" + Character.SIZE / 8);
System.out.println("Short.SIZE=" + Short.SIZE / 8);
System.out.println("Integer.SIZE=" + Integer.SIZE / 8);
System.out.println("Long.SIZE=" + Long.SIZE / 8);
System.out.println("Float.SIZE=" + Float.SIZE / 8);
System.out.println("Double.SIZE=" + Double.SIZE / 8);
}
}
-
深刻理解如何計算物件大小及指標壓縮
(之后文章我將為大家詳細解釋) -
深刻理解JVM記憶體模型
-
動態物件年齡判斷
JVM并不是永遠地要求物件的年齡必須達到了MaxTenuringThreshold才能晉升老年代,如果在Survivor空間中相同年齡所有物件大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的物件就可以直接進入老年代,無須等到MaxTenuringThreshold中要求的年齡,
垃圾判斷演算法
gc程序中需要知道哪些物件能回收,哪些物件不能回收,那如何判斷呢?就是靠這個演算法,這里我就不展開講了,學過JVM實戰的同學我相信對這兩種演算法都很了解了,
1、參考計數法
2、可達性分析演算法(GC Root)
如何調優
這里我們以記憶體32G服務器來計算,
JVM的堆區最大值默認是物理記憶體的四分之一,即8G,新生代、老年代的比例默認是1:2,即新生代約等于2.7G,老年代約等于5.4G,新生代中Eden區、From區、To區的比例是8:1:1,即Eden區約等于2.2G,From區、To區各占約270M,
前面我們計算了億級流量并發系統在高峰期時Eden區每秒產生大約200M的物件,如果在部署系統時未對JVM做任何調優,那么系統運行11s左右(2200M),Eden區就會被充滿,就會產生young gc,一般來說整個付款環節3s完成算快的,我們這里就按3s來計算,那么在產生young gc時,Eden區有約600M的物件無法回收,因為600M已超過To區的大小,會觸發空間擔保機制,這600M的物件會直接被移入老年代,按照這個節奏,程式運行一分半種多點就會產生full gc,引起STW,這在付款環節是不可接受的,所以我們需要做調優,
那如何做調優呢?我們需要反向來推理:為了保證不觸發空間擔保機制,From區、To區都設定成600M,那個新生代就是6G、老年代就是12G,這樣一個JVM就吃掉了18G的記憶體,所以還需要做其他事情:將這個服務器上其他比較吃記憶體的服務移走,以保證其他服務的正常運行,這里你為了保險起見,你可以設定得更大,具體調優的數值各位童鞋視實際情況而定吧,
這里有個注意點:看網上有些老師的視頻或文章, 他們調優的時候將老年代設定的大于新生代,我覺得這個方案非常危險, 道理同學們可以自己想想,我不建議同學們這樣做,
這篇文章就把預估調優堆區這件事情講清楚了,下篇咱們再將線上產生OOM調優講清楚,
如果你看到這里,那么恭喜你,對JVM的預估調優GET到了正確的姿勢,喜歡本篇文章就請關注+三連支持下博主吧!有問題也可以發到留言區,博主將會給大家解疑,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/188032.html
標籤:其他
