1、指定垃圾收集器
- -XX:+UseSerialGC 指定使用串行垃圾收集器,新生代及老年代都是串行收集,在大堆或者多核cpu的環(huán)境中不大適合使用該種垃圾收集器
- -XX:+UseParallelGC,并行垃圾收集器,新生代使用并行收集,老年代使用串行收集
- -XX:+UseParallelOldGC,并行垃圾收器,新生代使用并行手機,老年代使用并行收集,在一些低版本的JVM該參數(shù)不支持或者不生效,這兩種垃圾收集適合不在乎延時需要高吞吐的環(huán)境下使用,比如說一些批處理程序,
- -XX:+UseConcMarkSweepGC,并發(fā)垃圾收集,新生代使用并行收集,老年代采用并發(fā)收集,適合低延遲應用使用該種垃圾器。使用并發(fā)垃圾收集,相當于自動添加了參數(shù)-XX:+UseParNewGC
- -XX:+UseG1GC,使用G1垃圾收集,該種垃圾收集器比較復雜,目前在此種垃圾收集的調優(yōu)經(jīng)驗較少,在需要大堆的應用可以考慮使用,相比并發(fā)垃圾收集器,其產(chǎn)生的碎片更少,且可以通過參數(shù)控制垃圾收集過程中的停頓時間。
2、調整堆大小
在描述調整堆大小參數(shù),先用一張圖描述JVM堆的分布情況:

其中新生代由Eden和s0和s1組成,有時候s0也叫做from,s1叫做to?,F(xiàn)代JVM都是將堆分成幾個不同的區(qū)間,劃分的方法主要是根據(jù)對象的存活時間長短將整個jvm內存分為新生代,老年代、永久代。相應的主要參數(shù)有:
- -Xms<n>[g|m|k]:調整堆的最小大小,比如:-Xms1G,表示堆最小為1G,在系統(tǒng)啟動階段JVM直接向系統(tǒng)申請1G內存,此參數(shù)相當于參數(shù)-XX:InitialHeapSize=n[g|m|k];
- -Xmx<n>[g|m|k]:調整堆的最大大小,比如:-Xms1G表示堆最大為1G,此參數(shù)相當于參數(shù)-XX:MaxHeapSize=n[g|m|k];
- -XX:MinHeapFreeRatio=n,指定堆最小空余比例,當堆中空余內存比例小于該參數(shù)指定值時,JVM將增大內存大小直到最大堆大小,默認40;
- -XX:MaxHeapFreeRatio=n,指定堆最大空余比例,當堆中空余比例大于該參數(shù)制定值,JVM將減少內存大小,直到最小堆大小,默認為70;
JVM將在發(fā)生FGC之后,根據(jù)這兩個參數(shù)調整整個對的大小,F(xiàn)GC一般消耗的時間較多,為了降低延遲,一般將最小堆大小和最大堆大小設置成一樣的。 - -XX:NewRatio=n,老年代與新生代內存占比,-XX:NewRatio=4表示年輕代是老年代內存的1/4,也就是說年輕代占用總堆內存的1/5;
- -XX:SurvivorRatio=n,Eden區(qū)與1個survivor區(qū)域內存占比,-XX:SurvivorRatio=8,表示EdenEden區(qū)域的內存是一個survivor區(qū)域內存的8倍,一共兩個survivor區(qū)域,因此survivor占用新生代的1/10;
- -XX:PermSize=<n>[g|m|k],-XX:MaxPermSize=<n>[g|m|k],這兩個參數(shù)用于制定永久代內存大小,這個兩個參數(shù)在jdk8中已經(jīng)失效;
- -XX:MetaspaceSize=<n>[g|m|k], -XX:MaxMetaspaceSize=<n>[g|m|k],在jdk中使用這兩個參數(shù)指定元數(shù)據(jù)空間大??;
- -XX:TargetSurvivorRatio=n,設定survivor區(qū)的目標使用率,默認是50%;
- -XX:MaxDirectMemorySize=<n>[g|m|k],設置直接內存的大小,在使用一些nio框架時最好設置一下此參數(shù),此參數(shù)如果過小也有可能導致頻繁觸發(fā)FGC;
- -Xss<n>[g|m|k],指定Java線程棧的大小,默認值受環(huán)境影響,Xss越大,進程能運行的最大線程數(shù)就少,如果設置的過小,容易導致StackOverflowError錯誤;
3、收集gc日志
- -verbose:gc,輸出gc日志信息,默認輸出到標準輸出,參數(shù)與-XX:+PrintGCDetails作用一致;
- -XX:+PrintGCDetails,打印gc日志,打印的出日志包含日志收集原因,歌區(qū)域變化情況,以及用時;
- -XX:+PrintGCDateStamps,日志中輸出時間戳;
- -XX:+PrintGCTimeStamps,日志中輸出時間戳,與PrintGCDateStamps參數(shù)不同的地方在于,此參數(shù)輸出的時間是相對與應用啟動時間的差值;
- -Xloggc:filename,把gc日志信息輸入到指定文件中,
- -XX:+PrintGCApplicationStoppedTime,打印垃圾收集期間應用被暫停的時間;
- -XX:+PrintGCApplicationConcurrentTime,垃圾收集之前打印出應用未中斷的執(zhí)行時間;
- -XX:+UseGCLogFileRotation, -XX:GCLogFileSize=n,這兩個參數(shù)用于設置gc文件滾動和設置滾動日志文件的個數(shù),為了防止單個gc日志文件太大,生產(chǎn)上建議加上這兩個參數(shù);
4、CMS垃圾收集器參數(shù)
調優(yōu)CMS垃圾收集器比調優(yōu)吞吐量垃圾收集器復雜許多,下面列出一些用于設置CMS收集器的參數(shù):
- -XX:CMSInitiatingOccupancyFraction=n,設置CMS垃圾收集器啟動回收老年代的時機,當老年代對象占比超過n時,就啟動一次CMS回收周期,注意:這個參數(shù)只是設定首次CMS垃圾回收;
- -XX:+UseCMSInitiatingOccupancyOnly,使用這個參數(shù)可以使CMS一直按照CMSInitiatingOccupancyFraction設定的值啟動;
- -XX:CMSInitiatingPermOccupancyFraction=n,設置永久代內存占比超過n啟動cms回收,此參數(shù)需要配合參數(shù)-XX:+CMSClassUnloadingEnabled一起使用(Java8默認開啟);
- -XX:+CMSParallelInitialEnabled, 用于開啟CMS initial-mark階段采用多線程的方式進行標記,用于提高標記速度,在Java8開始已經(jīng)默認開啟;
- -XX:+CMSParallelRemarkEnabled,用戶開啟CMS remark階段采用多線程的方式進行重新標記,默認開啟;
- -XX:CMSFullGCsBeforeCompaction,我們知道cms垃圾收集器會產(chǎn)生內存碎片,該參數(shù)用于設置多少次CMS gc之后進行一次內存壓縮;該參數(shù)默認值為0;
- -XX:+ExplicitGCInvokesConcurrent 、-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses這兩個參數(shù)用戶指定hotspot虛擬在執(zhí)行System.gc()時使用CMS周期;
- -XX:+CMSScavengeBeforeRemark,強制hotspot虛擬機在cms remark階段之前做一次minor gc,用于提高remark階段的速度;
- -XX:ConcGCThreads=n,設置并發(fā)垃圾收集的線程數(shù),默認該值是基于ParallelGCThreads計算出來的;
- -XX:+CMSPrecleaningEnabled,指定CMS是否需要進行Pre cleaning這個階段,這個階段的主要工作是:1、在并發(fā)標記階段階段由于業(yè)務線程同步在跑,新生代中對象有可能引用到老年代的對象,產(chǎn)生新的引用關系,這個時候需要重新標記出老年代中被引用的對象;2、老年代中對象引用關系發(fā)生變化,這個需要把引用關系發(fā)生變化的卡表(card table)標記為dirty,提高remark標記的效率;
- -XX:CMSMaxAbortablePrecleanTime=n,設置cms abortable pre階段最長持續(xù)時間,單位為s,默認值5s。abortable prelean階段默認在Eden區(qū)大于2M啟動(通過參數(shù):-XX:CMSScheduleRemarkEdenSizeThreshold =n設置),直到Eden區(qū)域占用率超過50%(通過參數(shù):-XX:CMSScheduleRemarkEdenPenetration=n)時結束,同時還可以指定參數(shù)-XX:CMSMaxAbortablePrecleanLoops=n設置該階段的循環(huán)次數(shù)(默認0,表示不限制次數(shù)),為了防止abortable pre clean階段陷入無限等待,故設置一個最長的持續(xù)時間,達到最長持續(xù)時間之后,該階段終止,進入remark階段;
5、其他參數(shù)
-XX:+DisableExplicitGC,禁止hotspot執(zhí)行System.gc(),默認禁用;
-XX:+ScavengeBeforeFullGC,在執(zhí)行full gc之前執(zhí)行一次minor gc,默認開啟;
-XX:MaxTenuringThreshold=n,設定新生代的對象在經(jīng)歷多少次minor gc之后將轉移到老年代,默認是15;
-XX:+HeapDumpOnOutOfMemoryError,在hotspot發(fā)生OOM時打印出堆,默認打印到應用工作目錄,文件名稱為java_pid%p.hprof;
-XX:HeapDumpPath=xx,指定hotspot虛擬機OOM時堆轉儲文件的path;
-XX:ErrorFile=xx,設置JVM crash時生成crash文件的路徑,默認為./hs_err_pid%p.log;
-XX:+ParallelRefProcEnabled,采用多線程的方式發(fā)現(xiàn)需要處理的finalize方法的對象,非多線程執(zhí)行對象的finalize方法;
-XX:ParallelGCThreads=n,設置并行收集的線程數(shù),默認值采用Runtime.getRuntime().availableProcessors()來確定。不過是建立在返回值小于等于8的情況下,反之,會使用Runtime.availableProcessors()*5/8作為線程數(shù)。
-XX:+UseAdaptiveSizePolicy,開啟自適應調整JAVA內存策略,在使用吞吐量垃圾收集器時,該參數(shù)用于讓jvm自行調整Eden區(qū)和Survivor區(qū)空間的大小,新生代與老年代空間的大?。?/p>
-XX:+PrintAdaptiveSizePolicy,可以打印出survivor的一些詳細信息,關于survivor區(qū)間是否溢出,是否有對象轉移到老年代;
-XX:+PrintTenuringDistribution,顯示出survivor區(qū)間有效對象的年齡分布情況,可以通過該參數(shù)的輸出確定出survivor大小以及MaxTenuringThreshold的值;
-XX:MaxGCPauseMillis=n,設置最大gc停頓時間,這個時間不是設置的越小越好,此參數(shù)只在ps收集器上有效,不建議修改此參數(shù)的值;
-XX:GCTimeRatio,設置垃圾收集時間占總時間的比率,默認為99,也就是說垃圾收集時間占用總時間的1%,此參數(shù)只在ps收集器上有效;
-XX:+HeapDumpAfterFullGC,在執(zhí)行一次FGC之后打印出Heap到文件;
-XX:+HeapDumpBeforeFullGC,在執(zhí)行一次FGC之前打印出Heap到文件,這兩個參數(shù)主要用于調試;
-XX:+PrintHeapAtGC,打印gc前后堆詳細信息,不管是minor gc還是full gc都會打??;
-XX:+GCLockerInvokesConcurrent,在執(zhí)行gc之前都需要先先查看 gc locker是否被java線程持有,如果存在gc locker被持有的情況則忽略此次gc,在所有java線程完全釋放gc locker之后補償一次gc。此參數(shù)指定,如果是fgc的話,則補償一次CMS back groud fgc。
-XX:ReservedCodeCacheSize=<n>[g|m|k]、-XX:InitialCodeCacheSize=<n>[g|m|k],指定代碼緩存的大小,用于保存已編譯方法生成的本地代碼,如果代碼緩存被占滿JVM會發(fā)出警告信息,并切換到interpreted-only模式,JIT編譯器被停用,字節(jié)碼將不會再編譯成機器碼。這樣的話對JVM的性能影響很大;
-XX:+UseCodeCacheFlushing,如果代碼緩存不斷增長導致代碼緩存空間不夠,使用該參數(shù)讓jvm放棄一些被編譯的代碼,避免代碼緩存被占滿時JVM切換到interpreted-only的情況;
-
-XX:+DoEscapeAnalysis,開啟逃逸分析,逃逸分析是一種分析對象范圍的技術,在一些情況一個線程分配的對象可能會被其他對象使用,這種現(xiàn)象叫做“逃逸”,如果一個對象沒有“逃逸”,則可以運用一些額外的優(yōu)化技術,這種優(yōu)化技術“逃逸分析”。通過“逃逸分析” JIT可以使用如下技術優(yōu)化:
- 棧上分配
- 消滅同步
- 消滅垃圾回收讀寫障礙
- 對象爆炸
-XX:+UseBiasedLocking,開啟偏向鎖,偏向鎖是是鎖偏愛上次使用它的線程,在非競爭鎖的場景下,可以實現(xiàn)無鎖的開銷。
-XX:+UseLargePages,開啟使用大頁面,使用大頁面可以提高TLB(translation lookaside buffer(轉換后備緩存區(qū)))的緩存命中率;
-XX:PretenureSizeThreshold=n,指定對象占用的字節(jié)數(shù)超過n之后直接在老年代中分配,默認值0,表示最大值;
-XX:+OmitStackTraceInFastThrow,一些頻繁拋出的異常,JVM為了性能優(yōu)化而拋出沒有堆棧的異常,默認開啟,
-XX:+PrintFlagsInitial、-XX:+PrintFlagsFinal,打印出設置的JVM參數(shù)和最終生效的JVM參數(shù)和他們的值,-XX:+PrintCommandLineFlags打印出被修改的JVM參數(shù),一般在啟動應用添加此參數(shù)打印出引用啟動時添加的JVM參數(shù),在應用運行過程中一些JVM參數(shù)可能會被修改掉(通過jinfo工具);
-XX:+PrintSafepointStatistics,打印安全點統(tǒng)計信息,-XX:PrintSafepointStatisticsCount=n設置打印安全點統(tǒng)計信息的次數(shù);
-XX:+PrintReferenceGC,打印gc時處理的reference情況;
-XX:+UseCompressedOops,開啟壓縮指針,壓縮指針主要是為了解決32位操作系統(tǒng)內存尋址范圍只有4G的限制,開啟壓縮指針最大尋址范圍增加到32G;如果內存超過32G(Xmx>32G),開啟該參數(shù)無效;
-XX:+ParallelRefProcEnabled,使用此參數(shù)激活多線程方式的引用處理,截止到目前的jdk8該參數(shù)默認關閉,如果在gc中發(fā)現(xiàn)處理ref處理的時間過長,可以通過參數(shù)-XX:+PrintReferenceGC打印出每次垃圾收集中記錄每個引用對象類型的統(tǒng)計數(shù)據(jù),另外如果發(fā)現(xiàn)有大量軟引用正在被處理,可以使用參數(shù)-XX:SoftRefLRUPolicyPerMB調整軟件用的處理策略,該參數(shù)的默認值為1000(ms),該參數(shù)的含義是,使用該參數(shù)設置的值乘以java堆可用空間(以兆為單位)的出來的值,若軟引用在這段時間內沒有被訪問,那么這些軟引用將會被回收,調優(yōu)引用處理的目標主要有兩點:第一是降低引用在gc過程中處理時間,第二是降低heap的占用空間,減少垃圾收集頻率和最終需要復制的時間;
總結
本文列舉了一些常用的JVM參數(shù),至于如何調整各參數(shù)已達到JVM的最佳性能要以場景而定,最后來一張圖(來自JVM Internals)說明JVM運行狀態(tài):
