小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

優(yōu)化JVM年輕代垃圾回收參數(shù)?

 拼命奮斗的自己 2020-08-10

1、案例背景引入

按照慣例,我們接下來會用案例驅動來帶著大家分析到底該如何在特定場景下,預估系統(tǒng)的內存使用模型。

然后合理優(yōu)化新生代、老年代、Eden和Survivor各個區(qū)域的內存大小。

接著再盡量優(yōu)化參數(shù)避免新生代的對象進入老年代,盡量讓對象留在新生代里被回收掉。

我們這里的背景是電商系統(tǒng),電商系統(tǒng)其實一般會拆分為很多的子系統(tǒng)獨立部署

比如商品系統(tǒng)、訂單系統(tǒng)、促銷系統(tǒng)、庫存系統(tǒng)、倉儲系統(tǒng)、會員系統(tǒng),等等

我們這里就以比較核心的訂單系統(tǒng)作為例子來說明。

(提示:食用本案例之前,請務必充分理解專欄之前兩周的文章?。?/span>

我們的案例背景是每日上億請求量的電商系統(tǒng),那么大家可以來推算一下每日上億請求量的電商系統(tǒng),他會每日有多少活躍用戶?

一般按每個用戶平均訪問20次來計算,那么上億請求量,大致需要有500萬日活用戶。

那么繼續(xù)來推算一下,這500萬的日活用戶都是會進來進行大量的瀏覽,那么多少人會下訂單?

這里可以按照10%的付費轉化率來計算,每天大概有50萬人會下訂單,那么大致就是每天會有50萬訂單。

這50萬訂單算他集中在每天4小時的高峰期內,那么其實平均下來每秒鐘大概也就幾十個訂單,大家是不是覺得根本沒啥可說的?

因為幾十個訂單的壓力下,根本就不需要對JVM多關注,基本上就是每秒鐘占用一些新生代內存,隔很久新生代才會滿。然后一次Minor GC后垃圾對象清理掉,內存就空出來了,幾乎無壓力。

2、特殊的電商大促場景

但是如果你要是考慮到特殊的電商大促場景,就不會這么想了

因為很多中小型的電商平臺,確實平時系統(tǒng)壓力其實沒那么大,也沒太大的高并發(fā),每秒幾千并發(fā)壓力就算是高峰壓力了。

但是如果遇到一些大促場景,比如雙11什么的,情況就不同了。

假設在類似雙11的節(jié)日里,零點的時候,很多人等著大促開始就要剁手購物,這個時候,可能在大促開始的短短10分鐘內,瞬間就會有50萬訂單。

那么此時每秒就會有接近1000的下單請求,我們就針對這種大促場景來對訂單系統(tǒng)的內存使用模型分析一下。

3、抗住大促的瞬時壓力需要幾臺機器?

那么要抗住大促期間的瞬時下單壓力,訂單系統(tǒng)需要部署幾臺機器呢?

基本上可以按3臺來算,就是每臺機器每秒需要抗300個下單請求。這個也是非常合理的,而且需要假設訂單系統(tǒng)部署的就是最普通的標配4核8G機器。

從機器本身的CPU資源和內存資源角度,抗住每秒300個下單請求是沒問題的。

但是問題就在于需要對JVM有限的內存資源進行合理的分配和優(yōu)化,包括對垃圾回收進行合理的優(yōu)化,讓JVM的GC次數(shù)盡可能最少,而且盡量避免Full GC,這樣可以盡可能減少JVM的GC對高峰期的系統(tǒng)新更難的影響。

4、大促高峰期訂單系統(tǒng)的內存使用模型估算

背景已經全部說完了,接下來咱們就得來預估訂單系統(tǒng)的內存使用模型了.

基本上可以按照每秒鐘處理300個下單請求來估算,其實無論是訂單處理性能還是并發(fā)情況,都跟生產很接近

因為處理下單請求是比較耗時的,涉及很多接口的調用,基本上每秒處理100~300個下單請求是差不多的。

那么每個訂單咱們就按1kb的大小來估算,單單是300個訂單就會有300kb的內存開銷

然后算上訂單對象連帶的訂單條目對象、庫存、促銷、優(yōu)惠券等等一系列的其他業(yè)務對象,一般需要對單個對象開銷放大10倍~20倍。

此外,除了下單之外,這個訂單系統(tǒng)還會有很多訂單相關的其他操作,比如訂單查詢之類的,所以連帶算起來,可以往大了估算,再擴大10倍的量。

那么每秒鐘會有大概300kb * 20 * 10 = 60mb的內存開銷。

但是一秒過后,可以認為這60mb的對象就是垃圾了,因為300個訂單處理完了,所有相關對象都失去了引用,可以回收的狀態(tài)。

大家看下圖:

 

3f47162cd98.jpeg

 

5、內存到底該如何分配?

假設我們有4核8G的機器,那么給JVM的內存一般會到4G,剩下幾個G會留點空余給操作系統(tǒng)之類的來使用

不要想著把機器內存一下子都耗盡,其中堆內存我們可以給3G,新生代我們可以給到1.5G,老年代也是1.5G。

然后每個線程的Java虛擬機棧有1M,那么JVM里如果有幾百個線程大概會有幾百M

然后再給永久代256M內存,基本上這4G內存就差不多了。

同時還要記得設置一些必要的參數(shù),比如說打開“-XX:HandlePromotionFailure”選項(不熟悉這個參數(shù)的,可以回頭復習一下專欄之前的文章)

JVM參數(shù)如下所示:

“-Xms3072M -Xmx3072M -Xmn1536M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:HandlePromotionFailure”

但是“-XX:HandlePromotionFailure”參數(shù)在JDK 1.6以后就被廢棄了,所以現(xiàn)在一般都不會在生產環(huán)境里設置這個參數(shù)了。

在JDK 1.6以后,只要判斷“老年代可用空間”> “新生代對象總和”,或者“老年代可用空間”> “歷次Minor GC升入老年代對象的平均大小”

上述兩個條件滿足一個,就可以直接進行Minor GC,不需要提前觸發(fā)Full GC了。

所以實際上,如果大家用的是JDK 1.7或者JDK 1.8,那么JVM參數(shù)就保持如下即可,后面也都不再加入這個參數(shù)了:

“-Xms3072M -Xmx3072M -Xmn1536M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M”

此時JVM內存入下圖所示。

 

67205b1b145.jpeg

 

接著就很明確了,訂單系統(tǒng)的系統(tǒng)程序在大促期間不停的運行,每秒處理300個訂單,都會占據(jù)新生代60MB的內存空間

但是1秒過后這60MB對象都會變成垃圾,那么新生代1.5G的內存空間大概需要25秒就會占滿,如下圖。

 

9ddd073af38.jpeg

 

25秒過后就會要進行Minor GC了,此時因為有“-XX:HandlePromotionFailure”選項,所以你可以認為需要進行的檢查,主要就是比較“老年代可用空間大小”和“歷次Minor GC后進入老年代對象的平均大小”,剛開始肯定這個檢查是可以通過的。

所以Minor GC直接運行,一下子可以回收掉99%的新生代對象,因為除了最近一秒的訂單請求還在處理,大部分訂單早就處理完了,所以此時可能存活對象就100MB左右。

但是這里問題來了,如果“-XX:SurvivorRatio”參數(shù)默認值為8,那么此時新生代里Eden區(qū)大概占據(jù)了1.2GB內存,每個Survivor區(qū)是150MB的內存,如下圖。

 

5c7bb23f85d.jpeg

 

所以Eden區(qū)1.2GB滿了就要進行Minor GC了,因此大概只需要20秒,就會把Eden區(qū)塞滿,就要進行Minor GC了。

然后GC后存活對象在100MB左右,會放入S1區(qū)域內。如下圖。

 

8ea554f39e0.jpeg

 

然后再次運行20秒,把Eden區(qū)占滿,再次垃圾回收Eden和S1中的對象,存活對象可能還是在100MB左右會進入S2區(qū),如下圖。

 

b35f65950e0.jpeg

 

此時JVM參數(shù)如下:

“-Xms3072M -Xmx3072M -Xmn1536M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8”

6、新生代垃圾回收優(yōu)化之一:Survivor空間夠不夠

首先在進行JVM優(yōu)化的時候,第一個要考慮的問題,就是你通過估算,你的新生代的Survivor區(qū)到底夠不夠?

按照上述邏輯,首先每次新生代垃圾回收在100MB左右,有可能會突破150MB,那么豈不是經常會出現(xiàn)Minor GC過后的對象無法放入Survivor中?然后豈不是頻繁會讓對象進入老年代?

還有,即使Minor GC后的對象少于150MB,但是即使是100MB的對象進入Survivor區(qū),因為這是一批同齡對象,直接超過了Survivor區(qū)空間的50%,此時也可能會導致對象進入老年代。

(關于jvm的垃圾回收規(guī)則,如果不太清楚,請參加專欄之前的文章)

所以其實按照我們這個模型來說,Survivor區(qū)域是明顯不足的。

這里其實建議的是調整新生代和老年代的大小,因為這種普通業(yè)務系統(tǒng),明顯大部分對象都是短生存周期的,根本不應該頻繁進入老年代,也沒必要給老年代維持過大的內存空間,首先得先讓對象盡量留在新生代里。

所以此時可以考慮把新生代調整為2G,老年代為1G,那么此時Eden為1.6G,每個Survivor為200MB,如下圖。

 

77c17cb9135.jpeg

 

這個時候,Survivor區(qū)域變大,就大大降低了新生代GC過后存活對象在Survivor里放不下的問題,或者是同齡對象超過Survivor 50%的問題。

這樣就大大降低了新生代對象進入老年代的概率。

此時JVM的參數(shù)如下:

“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8”

其實對任何系統(tǒng),首先類似上文的內存使用模型預估以及合理的分配內存,盡量讓每次Minor GC后的對象都留在Survivor里,不要進入老年代,這是你首先要進行優(yōu)化的一個地方。

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

大家都知道,除了Minor GC后對象無法放入Survivor會導致一批對象進入老年代之外,還有就是有些對象連續(xù)躲過15次垃圾回收后會自動升入老年代。

其實按照上述內存運行模型,基本上20多秒觸發(fā)一次Minor GC,那么如果按照“-XX:MaxTenuringThreshold”參數(shù)的默認值15次來說,你要是連續(xù)躲過15次GC,就是一個對象在新生代停留超過了幾分鐘了,此時他進入老年代也是應該的。

有些博客會說,應該提高這個參數(shù),比如增加到20次,或者30次,其實那種說法根本是不對的

因為你對這個參數(shù)考慮必須結合系統(tǒng)的運行模型來說,如果躲過15次GC都幾分鐘了,一個對象幾分鐘都不能被回收,說明肯定是系統(tǒng)里類似用@Service、@Controller之類的注解標注的那種需要長期存活的核心業(yè)務邏輯組件。

那么他就應該進入老年代,何況這種對象一般很少,一個系統(tǒng)累計起來最多也就幾十MB而已。

所以你說你提高“-XX:MaxTenuringThreshold”參數(shù)的值,有啥用呢?讓這些對象在新生代里多停留幾分鐘?

因此考慮問題,一定不要人云亦云,要結合運行原理,自己推演和思考,不同的業(yè)務系統(tǒng)還都是不一樣的。

其實這個參數(shù)甚至你都可以降低他的值,比如降低到5次,也就是說一個對象如果躲過5次Minor GC,在新生代里停留超過1分鐘了,盡快就讓他進入老年代,別在新生代里占著內存了。

總之,對于這個參數(shù)務必是結合你的系統(tǒng)具體運行的模型來考慮。

要記住,JVM沒有萬能的最佳參數(shù),但是有一套通用的分析和優(yōu)化的方法。

此時JVM參數(shù)如下:

“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5”

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

另外有一個邏輯是說,大對象可以直接進入老年代 ,因為大對象說明是要長期存活和使用的

比如在JVM里可能會緩存一些數(shù)據(jù),這個一般可以結合自己系統(tǒng)中到底有沒有創(chuàng)建大對象來決定。

但是一般來說,給他設置個1MB足以,因為一般很少有超過1MB的大對象。如果有,可能是你提前分配了一個大數(shù)組、大List之類的東西用來放緩存的數(shù)據(jù)。

此時JVM參數(shù)如下:

“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M”

9、別忘了指定垃圾回收器

同時大家別忘了要指定垃圾回收器,新生代使用ParNew,老年代使用CMS,如下JVM參數(shù) :

“-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC”

ParNew垃圾回收器的核心參數(shù),其實就是配套的新生代內存大小、Eden和Survivor的比例

只要你設置合理,避免Minor GC后對象放不下Survivor進入老年代,或者是動態(tài)年齡判定之后進入老年代,給新生代里的Survivor充足的空間,那么Minor GC一般就沒什么問題。

然后根據(jù)你的系統(tǒng)運行模型,合理設置“-XX:MaxTenuringThreshold”,讓那些長期存活的對象,抓緊盡快進入老年代,別在新生代里一直待著。

這樣基本上一個初步的優(yōu)化好的JVM參數(shù)就結合你的業(yè)務出來了。明天我們繼續(xù)結合案例來分析 老年代的垃圾回收和參數(shù)優(yōu)化方式。

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多