1. 程式人生 > >1分鐘帶你入門JVM效能調優

1分鐘帶你入門JVM效能調優

版本:JDK8
一、閱讀前熱身:
1、瞭解jvm啟動流程:
這裡寫圖片描述
2、瞭解硬體、系統、程序三個層面的記憶體之間的概要記憶體分配,一張圖你就懂:
這裡寫圖片描述
3、下面是需要背住的重點,敲黑板!!堆記憶體分配,想了解引數的可以到最下面看下備註和建議:
先來個日誌(看不懂那就看圖):
這裡寫圖片描述

這裡寫圖片描述

備註:
Heap(堆記憶體)=eden+2survivor(年輕代)+ParOldGen(老生代)+Perm(jdk8以前)。

jdk8以後將永久代替換為MetaSpace(元空間)存在於本地記憶體。
from survivor 和 to survivor大小相同,且保證一個為empty.

二、實戰演練
1、JVM GC執行日誌:
這裡寫圖片描述


gc解析日誌:我們可以看到 由於年輕代的eden區100%,觸發了紅色框說由於記憶體分配失敗,所以gc回收,eden區gc後剩餘60567k(看年輕代藍色變化)存放於年輕代的survivor(看綠色survivor增長69%)。
紅色框解讀:
[PSYoungGen: 524800K->60567K(611840K)] 。格式為[PSYoungGen: a->b(c)].
PSYoungGen,表示新生代使用的是多執行緒垃圾收集器Parallel Scavenge。a為GC前新生代已佔用空間,b為GC後新生代已佔用空間。新生代又細分為一個Eden區和兩個Survivor區,Minor GC之後Eden區為空,b就是Survivor中已被佔用的空間。括號裡的c表示整個年輕代的大小。
524800K->60647K(2010112K),格式為x->y(z)。x表示GC前堆的已佔用空間,y表示GC後堆已佔用空間,z表示堆的總大小。
由新生代和Java堆佔用大小可以算出年老代佔用空間,此例中就是2010112K-611840K=1398272k。
[Times: user=0.46 sys=0.02, real=0.13 secs] 提供cpu使用及時間消耗,user是使用者態消耗的cpu時間,sys是系統態消耗的cpu時間,real是實際的消耗時間。
2、JVM gc執行過程:
這裡寫圖片描述

執行過程如上圖根據執行日誌順序,
第一步新生代記憶體分配區域eden空間100%後,無法分配記憶體。
第二步gc回收、保留一部分剩餘物件存放survivor,利用copy演算法始終保持一個為empty。併為物件age+1.
之間一直迴圈上述步驟,當age滿足很大的時候觸發老年代gc回收儲存到old 老年代區。
第四步由於不斷會有物件進入老年代,老年代記憶體會一直增大加上新生代達到臨界值記憶體最大值。觸發full gc進行整體回收。

疑問點:那你沒有說明元空間啊?元空間幹啥的呢?
元空間存放:class檔案、靜態物件、屬性等。而且在永久代的時候預設大小256m。但是在元空間jdk8後,它是jvm根據需要動態載入大小。

三、實戰後記憶:
網上一個很形象的例子描述物件在JVM堆記憶體中的生命週期:

我是一個普通的java物件,我出生在Eden區,在Eden區我還看到和我長的很像的小兄弟,我們在Eden區中玩了挺長時間。有一天Eden區中的人實在是太多了,我就被迫去了Survivor區的“To”區,自從去了Survivor區,我就開始漂泊了,因為Survivor的兩個區總是交換名字,所以我總是搬家,搬到To Survivor居住,搬來搬去,居無定所。直到我18歲的時候,爸爸說我成人了,該去社會上闖闖了。於是我就去了年老代那邊,年老代裡,人很多,並且年齡都挺大的,我在這裡也認識了很多人。在年老代裡,我生活了20年(每次GC加一歲),然後被回收。

四、效能調優建議:

jvm調優沒有一個固定模板配置說必須如何操作,它需要根據系統的情況不同對待。

但是可以有如下建議:

1、初始化記憶體和最大記憶體儘量保持一致,避免記憶體不夠用繼續擴充記憶體。最大記憶體不要超過實體記憶體,例如記憶體8g,你可以設定最大記憶體4g/6g但是不能超過8g否則載入類的時候沒有空間會報錯。

2、gc/full gc頻率不要太高、每次gc時間不要太長、根據系統應用來定。

五、演算法描述:

引用計數(Reference Counting):

比較古老的回收演算法。原理是此物件有一個引用,即增加一個計數,刪除一個引用則減少一個計數。垃圾回收時,只用收集計數為0的物件。此演算法最致命的是無法處理迴圈引用的問題。

標記-清除(Mark-Sweep):

這裡寫圖片描述

此演算法執行分兩階段。第一階段從引用根節點開始標記所有被引用的物件,第二階段遍歷整個堆,把未標記的物件清除。此演算法需要暫停整個應用,同時,會產生記憶體碎片。

複製(Copying):
這裡寫圖片描述

此演算法把記憶體空間劃為兩個相等的區域,每次只使用其中一個區域。垃圾回收時,遍歷當前使用區域,把正在使用中的物件複製到另外一個區域中。次演算法每次只處理正在使用中的物件,因此複製成本比較小,同時複製過去以後還能進行相應的記憶體整理,不會出現“碎片”問題。當然,此演算法的缺點也是很明顯的,就是需要兩倍記憶體空間。

標記-整理(Mark-Compact):

這裡寫圖片描述

此演算法結合了“標記-清除”和“複製”兩個演算法的優點。也是分兩階段,第一階段從根節點開始標記所有被引用物件,第二階段遍歷整個堆,把清除未標記物件並且把存活物件“壓縮”到堆的其中一塊,按順序排放。此演算法避免了“標記-清除”的碎片問題,同時也避免了“複製”演算法的空間問題。

六、本篇文章不足點:

此篇文章引數只設置了初始化記憶體和最大記憶體其餘都是系統自動分配,接下來會不斷進行優化配置,面試掌握上面的內容基本上就可以了。

寄語:被內推的同學能夠成功面試晉級、如果覺得自己現在不想去BATJ/TMD的、但是依舊想要換個工作的,也可以聯絡我哈 微信搜尋公眾號 【小誠信驛站】。