《深入理解JVM內幕之JVM簡單調優參數》要點:
本文介紹了深入理解JVM內幕之JVM簡單調優參數,希望對您有用。如果有疑問,可以聯系我們。
理想的情況下,一個Java程序使用JVM的默認設置也可以運行得很好,所以一般來說,沒有必要設置任何JVM參數.然而,由于一些性能問題(很不幸的是,這些問題經常出現),我們必須面對相關的JVM調優參數.
不管是YGC還是Full GC,GC過程中都會對導致程序運行中中斷,正確的選擇不同的GC策略,調整JVM、GC的參數,可以極大的減少由于GC工作,進而適當的提高Java程序的工作效率.但是調整GC是以個極為復雜的過程,由于各個程序具備不同的特點,并且跑在各個機器上的配置不同,所以使用的GC種類也會不同.
所有已制定的HotSpot內存管理和垃圾回收算法都基于一個相同的堆內存劃分:新生代(young generation)里存儲著新分配的和較年輕的對象,老年代(old generation)里存儲著長壽的對象.在此之外,永久代(permanent generation)存儲著那些需要伴隨整個JVM生命周期的對象.
下面我們就來看一下幾個簡單常用的JVM調優參數(-Xms、-Xmx、-XX:+HeapDumpOnOutOfMemoryError、-XX:HeapDumpPath、-XX:OnOutOfMemoryError、 -XX:PermSize、-XX:MaxPermSize……):
-Xms and -Xmx (or: -XX:InitialHeapSize and -XX:MaxHeapSize)
-Xms和-Xmx可以說是最流行的JVM參數,它們可以允許我們指定JVM的初始和最大堆內存大小.一般來說,這兩個參數的數值單位是Byte,但同時它們也支持使用速記符號,比如“k”或者“K”代表“kilo”,“m”或者“M”代表“mega”,“g”或者“G”代表“giga”.舉個例子,下面的命令啟動了一個初始化堆內存為128M,最大堆內存為2G,名叫“MyApp”的Java應用程序.
$ java -Xms128m -Xmx2g MyApp
在實際使用過程中,初始化堆內存的大小通常被視為堆內存大小的下界.然而JVM可以在運行時動態的調整堆內存的大小,所以理論上來說我們有可能會看到堆內存的大小小于初始化堆內存的大小.但是即使在非常低的堆內存使用下,我也從來沒有遇到過這種情況.這種行為將會方便開發者和系統管理員,因為我們可以通過將“-Xms”和“-Xmx”設置為相同大小來獲得一個固定大小的堆內存. -Xms和-Xmx實際上是-XX:InitialHeapSize和-XX:MaxHeapSize的縮寫.我們也可以直接使用這兩個參數,它們所起得效果是一樣的:
$ java -XX:InitialHeapSize=128m -XX:MaxHeapSize=2g MyApp
需要注意的是,所有JVM關于初始/最大堆內存大小的輸出都是使用它們的完整名稱:“InitialHeapSize”和“MaxHeapSize”.所以當你查詢一個正在運行的JVM的堆內存大小時,如使用-XX:+PrintCommandLineFlags參數或者通過JMX查詢,你應該尋找“InitialHeapSize”和“MaxHeapSize”標志而不是“Xms”和“Xmx”.
-XX:+HeapDumpOnOutOfMemoryError and -XX:HeapDumpPath
如果我們沒法為-Xmx(最大堆內存)設置一個合適的大小,那么就有可能面臨內存溢出(OutOfMemoryError)的風險,這可能是我們使用JVM時面臨的最可怕的猛獸之一.導致內存溢出的根本原因需要仔細的定位.通常來說,分析堆內存快照(Heap Dump)是一個很好的定位手段,如果發生內存溢出時沒有生成內存快照那就實在是太糟了,特別是對于那種JVM已經崩潰或者錯誤只出現在順利運行了數小時甚至數天的生產系統上的情況.
幸運的是,我們可以通過設置-XX:+HeapDumpOnOutOfMemoryError 讓JVM在發生內存溢出時自動的生成堆內存快照.有了這個參數,當我們不得不面對內存溢出異常的時候會節約大量的時間.默認情況下,堆內存快照會保存在JVM的啟動目錄下名為java_pid<pid>.hprof 的文件里(在這里<pid>就是JVM進程的進程號).也可以通過設置-XX:HeapDumpPath=<path>來改變默認的堆內存快照生成路徑,<path>可以是相對或者絕對路徑.
雖然這一切聽起來很不錯,但有一點我們需要牢記.堆內存快照文件有可能很龐大,特別是當內存溢出錯誤發生的時候.因此,我推薦將堆內存快照生成路徑指定到一個擁有足夠磁盤空間的地方.
-XX:OnOutOfMemoryError
當內存溢出發生時,我們可以執行一些指令,比如發個E-mail通知管理員或者執行一些清理工作.通過-XX:OnOutOfMemoryError 這個參數我們可以做到這一點,這個參數可以接受一串指令和它們的參數.比如在下面的例子中,當內存溢出錯誤發生的時候,我們可以將堆內存快照寫到/tmp/heapdump.hprof 文件并且在JVM的運行目錄執行腳本cleanup.sh
$ java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -XX:OnOutOfMemoryError ="sh ~/cleanup.sh" MyApp
-XX:PermSize and -XX:MaxPermSize
永久代在堆內存中是一塊獨立的區域,它包含了所有JVM加載的類的對象表示.為了成功運行應用程序,JVM會加載很多類(因為它們依賴于大量的第三方庫,而這又依賴于更多的庫并且需要從里面將類加載進來)這就需要增加永久代的大小.我們可以使用-XX:PermSize 和-XX:MaxPermSize 來達到這個目的.其中-XX:MaxPermSize 用于設置永久代大小的最大值,-XX:PermSize 用于設置永久代初始大小.下面是一個簡單的例子:
$ java -XX:PermSize=128m -XX:MaxPermSize=256m MyApp
請注意,這里設置的永久代大小并不會被包括在使用參數-XX:MaxHeapSize 設置的堆內存大小中.也就是說,通過-XX:MaxPermSize設置的永久代內存可能會需要由參數-XX:MaxHeapSize 設置的堆內存以外的更多的一些堆內存
-XX:InitialCodeCacheSize and -XX:ReservedCodeCacheSize
JVM一個有趣的,但往往被忽視的內存區域是“代碼緩存”,它是用來存儲已編譯方法生成的本地代碼.代碼緩存確實很少引起性能問題,但是一旦發生其影響可能是毀滅性的.如果代碼緩存被占滿,JVM會打印出一條警告消息,并切換到interpreted-only 模式:JIT編譯器被停用,字節碼將不再會被編譯成機器碼.因此,應用程序將繼續運行,但運行速度會降低一個數量級,直到有人注意到這個問題.就像其他內存區域一樣,我們可以自定義代碼緩存的大小.相關的參數是-XX:InitialCodeCacheSize 和-XX:ReservedCodeCacheSize,它們的參數和上面介紹的參數一樣,都是字節值.
-XX:+UseCodeCacheFlushing
如果代碼緩存不斷增長,例如,因為熱部署引起的內存泄漏,那么提高代碼的緩存大小只會延緩其發生溢出.為了避免這種情況的發生,我們可以嘗試一個有趣的新參數:當代碼緩存被填滿時讓JVM放棄一些編譯代碼.通過使用-XX:+UseCodeCacheFlushing 這個參數,我們至少可以避免當代碼緩存被填滿的時候JVM切換到interpreted-only 模式.不過,我仍建議盡快解決代碼緩存問題發生的根本原因,如找出內存泄漏并修復它.
關于調優我們一般有如下幾條經驗、規則:
新生代大小選擇:響應時間優先的應用盡可能設大,直到接近系統的最低響應時間限制(根據實際情況選擇).在此種情況下,新生代收集發生的頻率也是最小的,同時減少到達老年代的對象;吞吐量優先的應用盡可能的設置大,可能到達Gbit的程度,因為對響應時間沒有要求,垃圾收集可以并行進行,一般適合8CPU以上的應用;避免設置過小,當新生代設置過小時會導致:①YGC次數更加頻繁 ②可能導致YGC對象直接進入老年代,如果此時老年代滿了,會觸發FGC.
老年代大小選擇:響應時間優先的應用:老年代使用并發收集器,所以其大小需要小心設置,一般要考慮并發會話率和會話持續時間等一些參數,如果堆設置小了,可能會造成內存碎片、高回收頻率以及應用暫停而使用傳統的標記清除方式,如果堆大了,則需要較長的收集時間,最優化的方案一般需要參考以下數據獲得:并發垃圾收集信息、持久代并發收集次數、傳統GC信息、花在新生代和老年代回收上的時間比例;吞吐量優先的應用:一般吞吐量優先的應用都有一個很大的新生代和一個較小的老年代,因為這樣可以盡可能回收掉大部分短期對象,減少中期的對象,而老年代盡量存放長期存活對象.
較小堆引起的碎片問題:因為老年代的并發收集器使用標記清除算法所以不會對堆進行壓縮,當收集器回收時,他會把相鄰的空間進行合并,這樣可以分配給較大的對象,但是當堆空間較小時,運行一段時間以后就會出現"碎片",如果并發收集器找不到足夠的空間,那么并發收集器將會停止,然后使用傳統的標記清除方式進行回收,如果出現"碎片"可能需要進行如下配置:-XX:+UseCMSCompactAtFullCollection 使用并發收集器時,開啟對老年代的壓縮 -XX:CMSFullGCsBeforeCompaction=0 上面配置開啟的情況下,這里設置多少次Full GC后,對年老代進行壓縮.
用64位操作系統,Linux下64位的jdk比32位jdk要慢一些,但是吃得內存更多,吞吐量更大
Xmx和Xms設置一樣大,MaxPermSize和MinPermSize設置一樣大,這樣可以減輕伸縮堆大小帶來的壓力
使用CMS的好處是用盡量少的新生代,經驗值是128M-256M, 然后老年代利用CMS并行收集, 這樣能保證系統低延遲的吞吐效率. 實際上CMS的收集停頓時間非常的短,2G的內存, 大約20-80ms的應用程序停頓時間
系統停頓的時候可能是GC的問題也可能是程序的問題,多用jmap和jstack查看,或者killall -3 java,然后查看java控制臺日志,能看出很多問題.
仔細了解自己的應用,如果用了緩存,那么老年代應該大一些,緩存的HashMap不應該無限制長,建議采用LRU算法的Map做緩存,LRUMap的最大長度也要根據實際情況設定.
采用并發回收時,新生代要小一點,老年代要大一點,因為老年代用的是并發回收,即使時間長點也不會影響其他程序繼續運行,網站不會停頓
JVM參數的設置(特別是 –Xmx –Xms –Xmn -XX:SurvivorRatio -XX:MaxTenuringThreshold等參數的設置沒有一個固定的公式,需要根據PV old區實際數據 YGC次數等多方面來衡量.為了避免promotion faild可能會導致Xmn設置偏小,也意味著YGC的次數會增多,處理并發訪問的能力下降等問題.每個參數的調整都需要經過詳細的性能測試,才能找到特定應用的最佳配置.
附:
JVM參數的含義
并行收集器相關參數
CMS相關參數
輔助信息
--END
《深入理解JVM內幕之JVM簡單調優參數》是否對您有啟發,歡迎查看更多與《深入理解JVM內幕之JVM簡單調優參數》相關教程,學精學透。維易PHP學院為您提供精彩教程。
轉載請注明本頁網址:
http://www.snjht.com/jiaocheng/8000.html