JVM实用参数(四)内存调优
理想的情況下,一個Java程序使用JVM的默認設(shè)置也可以運行得很好,所以一般來說,沒有必要設(shè)置任何JVM參數(shù)。然而,由于一些性能問題(很不幸的是,這些問題經(jīng)常出現(xiàn)),一些相關(guān)的JVM參數(shù)知識會是我們工作中得好伙伴。在這篇文章中,我們將介紹一些關(guān)于JVM內(nèi)存管理的參數(shù)。知道并理解這些參數(shù),將對開發(fā)者和運維人員很有幫助。
所有已制定的HotSpot內(nèi)存管理和垃圾回收算法都基于一個相同的堆內(nèi)存劃分:新生代(young generation)里存儲著新分配的和較年輕的對象,老年代(old generation)里存儲著長壽的對象。在此之外,永久代(permanent generation)存儲著那些需要伴隨整個JVM生命周期的對象,比如,已加載的對象的類定義或者String對象內(nèi)部Cache。接下來,我們將假設(shè)堆內(nèi)存是按照新生代、老年代和永久代這一經(jīng)典策略劃分的。然而,其他的一些堆內(nèi)存劃分策略也是可行的,一個突出的例子就是新的G1垃圾回收器,它模糊了新生代和老年代之間的區(qū)別。此外,目前的開發(fā)進程似乎表明在未來的HotSpot JVM版本中,將不會區(qū)分老年代和永久代。
-Xms and -Xmx (or: -XX:InitialHeapSize and -XX:MaxHeapSize)
-Xms和-Xmx可以說是最流行的JVM參數(shù),它們可以允許我們指定JVM的初始和最大堆內(nèi)存大小。一般來說,這兩個參數(shù)的數(shù)值單位是Byte,但同時它們也支持使用速記符號,比如“k”或者“K”代表“kilo”,“m”或者“M”代表“mega”,“g”或者“G”代表“giga”。舉個例子,下面的命令啟動了一個初始化堆內(nèi)存為128M,最大堆內(nèi)存為2G,名叫“MyApp”的Java應(yīng)用程序。
| 1 | java -Xms128m -Xmx2g MyApp |
在實際使用過程中,初始化堆內(nèi)存的大小通常被視為堆內(nèi)存大小的下界。然而JVM可以在運行時動態(tài)的調(diào)整堆內(nèi)存的大小,所以理論上來說我們有可能會看到堆內(nèi)存的大小小于初始化堆內(nèi)存的大小。但是即使在非常低的堆內(nèi)存使用下,我也從來沒有遇到過這種情況。這種行為將會方便開發(fā)者和系統(tǒng)管理員,因為我們可以通過將“-Xms”和“-Xmx”設(shè)置為相同大小來獲得一個固定大小的堆內(nèi)存。 -Xms和-Xmx實際上是-XX:InitialHeapSize和-XX:MaxHeapSize的縮寫。我們也可以直接使用這兩個參數(shù),它們所起得效果是一樣的:
| 1 | $ java -XX:InitialHeapSize=128m -XX:MaxHeapSize=2g MyApp |
需要注意的是,所有JVM關(guān)于初始\最大堆內(nèi)存大小的輸出都是使用它們的完整名稱:“InitialHeapSize”和“InitialHeapSize”。所以當(dāng)你查詢一個正在運行的JVM的堆內(nèi)存大小時,如使用-XX:+PrintCommandLineFlags參數(shù)或者通過JMX查詢,你應(yīng)該尋找“InitialHeapSize”和“InitialHeapSize”標志而不是“Xms”和“Xmx”。
-XX:+HeapDumpOnOutOfMemoryError and -XX:HeapDumpPath
如果我們沒法為-Xmx(最大堆內(nèi)存)設(shè)置一個合適的大小,那么就有可能面臨內(nèi)存溢出(OutOfMemoryError)的風(fēng)險,這可能是我們使用JVM時面臨的最可怕的猛獸之一。就同另外一篇關(guān)于這個主題的博文說的一樣,導(dǎo)致內(nèi)存溢出的根本原因需要仔細的定位。通常來說,分析堆內(nèi)存快照(Heap Dump)是一個很好的定位手段,如果發(fā)生內(nèi)存溢出時沒有生成內(nèi)存快照那就實在是太糟了,特別是對于那種JVM已經(jīng)崩潰或者錯誤只出現(xiàn)在順利運行了數(shù)小時甚至數(shù)天的生產(chǎn)系統(tǒng)上的情況。
幸運的是,我們可以通過設(shè)置-XX:+HeapDumpOnOutOfMemoryError?讓JVM在發(fā)生內(nèi)存溢出時自動的生成堆內(nèi)存快照。有了這個參數(shù),當(dāng)我們不得不面對內(nèi)存溢出異常的時候會節(jié)約大量的時間。默認情況下,堆內(nèi)存快照會保存在JVM的啟動目錄下名為java_pid<pid>.hprof?的文件里(在這里<pid>就是JVM進程的進程號)。也可以通過設(shè)置-XX:HeapDumpPath=<path>來改變默認的堆內(nèi)存快照生成路徑,<path>可以是相對或者絕對路徑。
雖然這一切聽起來很不錯,但有一點我們需要牢記。堆內(nèi)存快照文件有可能很龐大,特別是當(dāng)內(nèi)存溢出錯誤發(fā)生的時候。因此,我們推薦將堆內(nèi)存快照生成路徑指定到一個擁有足夠磁盤空間的地方。
-XX:OnOutOfMemoryError
當(dāng)內(nèi)存溢發(fā)生時,我們甚至可以可以執(zhí)行一些指令,比如發(fā)個E-mail通知管理員或者執(zhí)行一些清理工作。通過-XX:OnOutOfMemoryError 這個參數(shù)我們可以做到這一點,這個參數(shù)可以接受一串指令和它們的參數(shù)。在這里,我們將不會深入它的細節(jié),但我們提供了它的一個例子。在下面的例子中,當(dāng)內(nèi)存溢出錯誤發(fā)生的時候,我們會將堆內(nèi)存快照寫到/tmp/heapdump.hprof 文件并且在JVM的運行目錄執(zhí)行腳本cleanup.sh
| 1 | $ java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof -XX:OnOutOfMemoryError ="sh ~/cleanup.sh"?MyApp |
?-XX:PermSize and -XX:MaxPermSize
永久代在堆內(nèi)存中是一塊獨立的區(qū)域,它包含了所有JVM加載的類的對象表示。為了成功運行應(yīng)用程序,JVM會加載很多類(因為它們依賴于大量的第三方庫,而這又依賴于更多的庫并且需要從里面將類加載進來)這就需要增加永久代的大小。我們可以使用-XX:PermSize?和-XX:MaxPermSize 來達到這個目的。其中-XX:MaxPermSize 用于設(shè)置永久代大小的最大值,-XX:PermSize 用于設(shè)置永久代初始大小。下面是一個簡單的例子:
| 1 | $ java -XX:PermSize=128m -XX:MaxPermSize=256m MyApp |
請注意,這里設(shè)置的永久代大小并不會被包括在使用參數(shù)-XX:MaxHeapSize?設(shè)置的堆內(nèi)存大小中。也就是說,通過-XX:MaxPermSize設(shè)置的永久代內(nèi)存可能會需要由參數(shù)-XX:MaxHeapSize 設(shè)置的堆內(nèi)存以外的更多的一些堆內(nèi)存。
-XX:InitialCodeCacheSize and -XX:ReservedCodeCacheSize
JVM一個有趣的,但往往被忽視的內(nèi)存區(qū)域是“代碼緩存”,它是用來存儲已編譯方法生成的本地代碼。代碼緩存確實很少引起性能問題,但是一旦發(fā)生其影響可能是毀滅性的。如果代碼緩存被占滿,JVM會打印出一條警告消息,并切換到interpreted-only 模式:JIT編譯器被停用,字節(jié)碼將不再會被編譯成機器碼。因此,應(yīng)用程序?qū)⒗^續(xù)運行,但運行速度會降低一個數(shù)量級,直到有人注意到這個問題。就像其他內(nèi)存區(qū)域一樣,我們可以自定義代碼緩存的大小。相關(guān)的參數(shù)是-XX:InitialCodeCacheSize 和-XX:ReservedCodeCacheSize,它們的參數(shù)和上面介紹的參數(shù)一樣,都是字節(jié)值。
-XX:+UseCodeCacheFlushing
如果代碼緩存不斷增長,例如,因為熱部署引起的內(nèi)存泄漏,那么提高代碼的緩存大小只會延緩其發(fā)生溢出。為了避免這種情況的發(fā)生,我們可以嘗試一個有趣的新參數(shù):當(dāng)代碼緩存被填滿時讓JVM放棄一些編譯代碼。通過使用-XX:+UseCodeCacheFlushing 這個參數(shù),我們至少可以避免當(dāng)代碼緩存被填滿的時候JVM切換到interpreted-only 模式。不過,我仍建議盡快解決代碼緩存問題發(fā)生的根本原因,如找出內(nèi)存泄漏并修復(fù)它。
原創(chuàng)文章,轉(zhuǎn)載請注明:?轉(zhuǎn)載自并發(fā)編程網(wǎng) – ifeve.com本文鏈接地址:?JVM實用參數(shù)(四)內(nèi)存調(diào)優(yōu)
總結(jié)
以上是生活随笔為你收集整理的JVM实用参数(四)内存调优的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java虚拟机学习(8):查看JVM参数
- 下一篇: jvm参数详解