原創聲明
本公眾號所有文章均原創,未經許可不得轉載(已與維權騎士籤約)。文章以圖解為主、代碼為輔。關注我,無限收穫Offer!
文:吳瀟/Java高級工程師
本文截取java後端服務最常用的jvm選項,逐一解釋它們的作用及JVM內存、GC、類加載等相關知識,建議收藏。為避免篇幅太長,本次介紹最小堆大小(-Xmsize)、最大堆大小(-Xmxsize),以及新生代大小(-Xmnsize)的設置。
相信很多java工程師在工作中都會接觸到jvm,在面試中也會被問到跟java虛擬機有關的問題。我們為了把工作做好、把項目維護好、在系統出現fullgc等問題的時候,能夠像老司機一樣準確定位問題,很有必要把java虛擬機知識學習一遍。
然而,Java虛擬機的知識量很大,買一本書回來慢慢學習會比較消耗時間(例如《深入理解Java虛擬機》,其實這本書也只算&34;)。況且jvm優質學習資源較少,只有官方文檔較好,但是官方文檔內容又太多了,不適合作為學習教材,而且還是英文的。
即使你下定決心,準備把Java虛擬機的所有知識徹底學一遍,也很有可能遇到這些問題:1)看到後面忘記前面;2)看的時候,沒有練習機會,導致在實際要用的時候,又不知道怎麼辦;3)看完之後,當時雖然記得,但是過了一段時間之後,又都忘掉了。
針對上面這些問題,我們應該從實踐出發,帶著目的學習。這樣不但記得牢、而且一開始就面向實踐,理解得也要比單純的看資料深入。在這篇文章中,就以我們公司的某個後端服務的java啟動選項為例,介紹這些jvm選項,逐個解釋這些選項的作用,及相關的JVM內存管理、GC或類加載等知識。
我們公司某一個大流量的後端服務配置了這些jvm選項(這些選項也是目前國內網際網路公司經常用到的),如下圖所示。
這個配置項對應的JVM選項是 -Xms<size>,其中,4G是參數值。
我們知道java命令是用來啟動java虛擬機執行java代碼的。java命令支持很多選項,在這些選項當中,以&34;開頭的,都是Java虛擬機選項;不以&34;開頭的選項,不是傳給Java虛擬機的,例如&34;中的&34;不是傳給JVM的。
所以,-Xms4G中的-X代表這是一個jvm選項,m代表memory,對於jvm而言memory就是堆;s是smallest,最小的意思。-Xms4G代表把JVM的堆的最小值設為4G。
jvm堆隨著java程序的運行不斷增大,因此,這裡的堆最小值也是jvm初始堆大小。當jvm運行一段時間後,堆大小超過初始值,這裡配置的值其實沒什麼用了。
所以,最小堆大小或初始堆大小隻影響JVM啟動階段,對後續jvm運行沒什麼影響。
那麼,在實際運用中,-Xms<size>應該配置成多少呢?假如伺服器是8核16G,先設置成10G,即物理內存的一半再多加一些(建議跟最大堆大小設置成相同值,這樣可以減少剛部署階段的fullgc次數),然後運行一段時間再看容器的監控,看容器還剩多少內存。如果還剩很多,再調大一些,例如設置成12G,直到充分利用容器物理內存為止。
對應的JVM選項是 -Xmx<size>,它跟前面-Xms<size>類似,配置方法完全一樣。
-Xmx4G中,m代表memory,x是maximum,最大的意思。-Xmx4G就是把JVM的堆的最大值設為4G。
前面說過,隨著java程序運行,堆會從初始值開始穩步增長,當達到最大值以後就不再增長,以後主要靠GC來回收內存。所以,堆最大值的設置要比最小值謹慎,配置小了,程序內存不夠用,頻繁GC;配置得太大,每次GC時間比較長,程序有停頓現象(也跟垃圾回收器的選擇有關)。對後端服務而言,堆最大值一般與堆最小值配置成一樣的即可。
實際工作中,-Xmx<size>的值怎麼選?按照前面-Xms<size>的配置方法操作即可。如果每次GC時間較長,說明堆配置值的大了,適當減小堆最大值。
這項配置對應的jvm選項是-Xmn<size>,即把young generation(新生代)設置為1G。
-Xmn<size>中,m代表memory(如前所述),字母n代表什麼呢?它代表nursery(託兒所,存放新出生的嬰兒,即新創建的對象)。-Xmn1G就是把新生代固定設置成1G。另外,新生代也可以像前面一樣,最大、最小值分開設置,用-XX:NewSize來設置新生代的初始大小(最小值),用-XX:MaxNewSize設置新生代的最大值,這裡-Xmn<size>相當於同時指定-XX:NewSize和-XX:MaxNewSize。
jvm運行一段時間以後,新生代的初始大小,即-XX:NewSize其實沒什麼作用了,起作用的僅僅是-XX:MaxNewSize,所以我們往往把兩者設置成一樣的值,而且用-Xmn<size>這種合併的寫法,這樣比較方便。
JVM內存知識:我們知道,java中新創建的對象都是放在新生代(young generation)中的,尤其是剛創建出來的對象,更是放在了新生代中的eden區。經歷過少數幾次GC的新對象往往都是放在survivor區。對於java這樣的&34;的語言來說,程序運行一旦運行起來,就不斷有大量的對象被新建和釋放(java一開始甚至因為運行得太慢,差點被淘汰)。因此,新生代的GC非常活躍,如果你們公司有實時監控新生代內存的系統,就可以看到,新生代幾乎一直在GC。新生代的GC雖然頻繁,但是所產生的停頓非常非常小,幾乎可以忽略不計,不會產生多少服務延遲。新生代GC也就是我們所說的Young GC。
那麼,-Xmn<size>怎麼設置呢?假如設置得太小,那麼Yong GC就會發生得更加頻繁,所產生的的效果就是,它會比較多的消耗CPU資源;而如果設置的太大,那麼新對象存活的時間就會比較長,導致它們被不恰當的放入老年代(old generation),從而導致產生太多full gc,而full gc就對服務有明顯影響了。Oracle官方文檔建議我們把新生代的大小設置為堆的總大小的1/2到1/4之間。但是,Oracle不了解我們的業務特點,我們還是需要根據業務監控,來看新生代具體要設置為多大。
那具體要怎麼調整?就是前面說的,如果Yong GC太多了或GC導致的CPU計算量太高,就調大新生代;而如果業務服務出現了full gc,那考慮調大新生代。不過,據我多年經驗總結,full gc很少是由JVM參數配置不當造成的,它往往是業務代碼寫得不合理導致的。如果你的某一個業務服務平時運行得很好,突然有一天full gc,就看看最近上線改了什麼。另外,如果full gc是因為服務連續、長時間運行導致,那就看你代碼裡面有沒有不當使用內存,有沒有內存洩漏,長期引用不需要的對象。
到這裡才發現,還有這些JVM參數沒有講呢。下一篇文章繼續分析,感興趣的朋友請關注哦。
-XX:+AlwaysPreTouch -XX:PermSize=256M -XX:MaxPermSize=256M
-XX:+UseConcMarkSweepGC -XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=80
-Dlog4j.configurationFile=/data/webapps/dealservice_bizer/current/conf/log4j2.xml
希望本文對您有幫助,如果您對網際網路、前/後/客戶端、架構/分布式/高可用/高並發/高實時、電商、Redis、MySQL、Zookeeper、Spring、Android、瀏覽器插件、Java、Java虛擬機、C/C++、Linux、個性化推薦、社區發現、機器學習、數據挖掘等感興趣,歡迎關注。