紙上得來終覺淺 絕知此事要躬行
我所在的公司基本上是沒有機會進行JVM參數調優的,可是若是有些東西本身不親身經歷一下,看再多的理論知識也只能算是紙上談兵,真正碰到問題的時候仍是不知道該怎麼分析。因此就本身製造一些問題而後看其現象,利用所學的知識事前推測,看現象是否是和本身推測的同樣。這樣不只對本身所學的知識又是一次鞏固,並且也能鍛鍊本身解決問題的能力(雖然問題是本身製造的)。html
其實在寫這篇文章以前已經看過好好幾遍關於JVM調優那一塊的內容,不管是書仍是博客,可是大都看完了感受本身懂了,可是真正本身模擬操做的時候又以爲什麼都不會,可是通過本身模擬一遍之後發現可以將以前的知識都關聯起來,造成了一個面,感受理解有深了一點。這裏強調一下但願你們看完之後,可以本身在機器上模擬一遍,採用不一樣的參數而後本身猜測結果並驗證java
工欲善其事,必先利其器。在分析JVM以前咱們須要先將工具準備一下,一個是可視化的垃圾回收工具,另外一個是壓測的工具。git
GcViews
代碼從Git上下載下來github地址 mvn clean install
target
文件夾,在裏面能夠找到gcviewer-1.37-SNAPSHOT.jar
文件Apache JMeter是一個開源的壓力測試具,JMeter是基於Java開發的,JMeter不只僅用於Web壓力測試,還用開源用於基於訪問式軟件作壓力測試,可對靜態文件、數據庫、FTP、SSH等作壓力測試
/Users/hupengfei/apache-jmeter-5.1.1
bin
目錄下面sh jmeter
而後裏面如何配置參數的話我這裏就不細說了,你們能夠看這篇文章JMeter Http 壓力測試【圖解】github
對於JVM調優來講,主要是對JVM垃圾收集的優化,通常來講是由於有問題了才須要優化,因此對於JVM的GC來講若是你觀察到你的應用服務進程的CPU使用率比較高,而且在GC日誌中發現GC次數比較頻繁、GC停頓時間長,這就代表你須要對GC進行優化了。算法
在對GC調優的過程當中,咱們不進行必要知道一些GC的原理,更重要要熟練使用各類可監控和分析的工具,具有GC調優的實戰能力。而目前來講使用率最高的兩款垃圾收集器有兩個一個是CMS一個是G1。從Java9開始,採用G1做爲默認的垃圾收集器,而G1的目標也是逐步要取代CMS。因此下面我簡單介紹一下這兩款收集器的區別。數據庫
可使用命令
java -XX:+PrintCommandLineFlags -version
在命令行查看輸出默認的一些參數。此處可查看各個版本默認的垃圾收集器
CMS收集器將Java堆分爲年輕代和年老代(在Java8中就已經去掉了永久代,轉爲了元空間,而元空間是直接存儲在內存中的,並不在JVM中)。這主要是由於有研究代表,超過百分之90的對象在第一次GC時就會被回收掉,可是少數對象會存活較長的時間。apache
CMS中還將年輕代分爲兩部分,一部分是倖存者空間(Survivor)和伊甸園空間(Eden)。新的對象始終在Eden空間上建立,一旦一個對象在一次垃圾收集後還倖存的話,就會被移動到倖存者空間。當一個對象在屢次垃圾收集後還存活,它會被移動到年老代。這樣作的目的是在年輕代和年老代採用不一樣的垃圾收集算法,已達到較高的收集效率。好比因爲年輕代的對象存活時間較短,一次垃圾回收遺留的對象較少,因此採用複製-整理算法。可是在老年代中,對象存活時間較長,有可能一次垃圾回收回收的對象較少,遺留的對象較多,因此採用標記-整理算法。json
與CMS相比,G1有兩大特色併發
圖中的U表示未分配的區域,G1將堆拆分紅小的區域,一個最大的好處就是可以作局部區域的垃圾回收,而不是每次要回收整個區域好比年輕代和年老代,這樣回收的停頓時間會比較短。收集過程大概以下app
我使用的版本是Java8,使用的Java垃圾回收器是CMS的
下面我經過實際的例子來實戰一下Java程序中因爲青年代設置太小,致使頻繁的GC,咱們將經過GC日誌分析工具來觀察GC活動並定位問題。
首先咱們創建一個SpringBoot的程序,做爲咱們的調優對象。代碼以下:
@RestController @Slf4j public class GcTestController { private List<Greeting> objListCache = new ArrayList<>(); @RequestMapping("/greeting") public Greeting greeting() { Greeting greeting = new Greeting(); if (objListCache.size() >= 100000) { log.info("clean the List!!!!!!!!!!"); objListCache.clear(); } else { objListCache.add(greeting); } return greeting; } } @Data class Greeting { private String message1; private String message2; private String message3; private String message4; private String message5; private String message6; private String message7; private String message8; private String message9; private String message10; private String message11; private String message12; private String message13; private String message14; private String message15; private String message16; private String message17; private String message18; private String message19; private String message20; }
上面代碼建立一個對象池,當對象池中的對象達到100000的時候纔會清空一次,用來模擬老年代的對象。這裏你們能夠利用我上一篇文章幾百萬數據放入內存不會把系統撐爆嗎?大概計算一下10W個對象放在內存中大概佔用多少內存。這裏我就直接說了10萬個Greeting
對象大概佔用10M的空間。
因此下面我在Idea中設置啓動參數設置,參數以下
-Xmx52m -Xmn9m -Xss256k -XX:+PrintGC -XX:+UseConcMarkSweepGC -Xloggc:/Users/hupengfei/Downloads/gclog/gc.log
我給程序設置的初始堆大小是52MB,設置的年輕代的大小爲9MB,年輕代中默認Eden區和Survior區比例是4:1,因此大概年輕代中Eden區大小爲7.2MB,目的是爲了讓你們看到在Eden區沒有回收的對象會進入到老年代,在Eden區滿了的話那麼就會發生Young GC。
而後咱們使用JMeter壓測工具向程序發送測試請求,注意這裏我設置的訪問時間是10分鐘,而後一個線程不間斷進行訪問。
十分鐘事後咱們可使用GCViewer工具打開GC日誌,咱們看到以下的這張圖
基於上面的圖所展示的,咱們能夠獲得一個結論,就是設置的年輕代不夠,爲何會得出這樣的結論呢?
經過GCView左邊的顯示,咱們能夠看到總GC發生了1622次其中Full GC發生一次。
接下來咱們在總堆大小不變的狀況下,咱們僅僅調整一下年輕代的大小,將其調整爲16MB,而後咱們再來看一下圖
咱們能夠看到雖然還有一次的Full GC 可是年輕代的GC並無那麼頻繁了。而且累計GC暫停的時間只有1.48秒
若是咱們還想繼續優化呢?就是繼續擴大堆內存的總大小,接下來咱們將堆設置爲200MB,年輕代設置爲80MB,咱們再來看一下效果。
能夠看到一樣時間內,已經沒有了Full GC,而且年輕代的GC發生更少了
針對於CMS收集器來講,咱們要設置合理的年輕代和年老代的大小,你可能會問有沒有一個固定的公式呢?其實我這裏並無,調優的過程是一個迭代的過程,能夠採用JVM的默認值,而後進行壓測分析GC日誌。觀察在不一樣狀況下GC的回收狀況。
若是咱們看到頻繁發生Minor GC,而頻繁GC效率又不高,說明咱們的對象並無那麼快被回收,這時候咱們能夠適當調大年輕代大小,而後觀察。
若是咱們看到年老代的內存使用率處在高位,致使頻繁的發生Full GC。這種通常分爲兩種狀況
已經將測試代碼放到了GitHub上https://github.com/modouxiansheng/Doraemon上,而且將我屢次試驗的GC日誌也給放進去了,你們不想本身試驗的能夠將GC日誌給下載下來本身看一下圖
筆者文筆功力尚淺,若有不妥,請慷慨指出,一定感激涕零
紙上的知識,或者說是書上或者網上的知識,終究仍是做者本身的經驗總結。必然有做者的思路。可是未必就與實際相結合,更重要的是一句話所要傳達的準確信息不是每一個人看過那種文字描述就能獲得的。若是偏偏有這方面的經歷就會產生共鳴。
我認爲,人讀書就是爲了學習,而學習也偏偏是爲了自身的成長。因此學習的中心在於人而不是書本。學習的本質就是在於要將本身所學的知識與自身相結合。若是不與自身相結合,本身不能對書本的知識產生共鳴,就很難深入理解書中的道理,天然也很難記住這種道理。
書中的知識,大可能是做者自身的理解和感悟,因此很難將這種讓做者共鳴的場景重現在讀者的腦海中,讓做者也產生共鳴,所以「紙上得來終覺淺」。只有解構書中的知識並與自身聯繫,「絕知此事要躬行」,那麼咱們在學習知識的同時也是在理解咱們自身,理解咱們所在的世界,得到心靈的共鳴,得到知識的鞏固。
因此我也會在我文章中再三的強調,若是你們想要對這方面知識更加深入的話,那麼必定要本身在機器上本身跑一遍,本身觀察一下,本身修改幾個參數,驗證一下狀況。有可能我碰到的坑你碰不到,你碰到的坑我沒碰到。那麼你碰到這個坑本身解決了就是對於本身能力的提高。
有感興趣的能夠關注一下我新建的公衆號,搜索[程序猿的百寶袋]。或者直接掃下面的碼也行。