1、為什麼要進行JVM優化?

在本地開發環境中我們很少會遇到需要對jvm進行優化的需求,但是到了生產環境,我們
可能將有下面的需求:

  • 執行的應用 “卡住了”,日誌不輸出,程式沒有反應
  • 伺服器的 CPU負載突然升高
  • 在多執行緒應用下,如何分配執行緒的數量?

        。。。。。

對jvm優化分析解決在生產環境中所遇到的各種“棘手”的問題。

在jvm中有很多的引數可以進行設定,這樣可以讓jvm在各種環境中都能夠高效的執行。絕大部分的引數保持預設即可。

注:使用的jdk版本為1.8。

2、三種引數型別

jvm的引數型別分為三類,分別是:

標準引數:

  • -help
  • -version

-X 引數 (非標準引數):

  • -Xint
  • -Xcomp

-XX 引數(使用率較高):

  • -XX:newSize
  • -XX:+UseSerialGC

3、標準引數

jvm的標準引數,一般都是很穩定的,在未來的JVM版本中不會改變,可以使用java -help 檢索出所有的標準引數。

  [[email protected] ~]# java ‐help
用法: java [‐options] class [args...]
           (執行類)
   或  java [‐options] ‐jar jarfile [args...]
           (執行 jar 檔案)
其中選項包括:
    ‐d32   使用 32 位資料模型 (如果可用)    
    ‐d64   使用 64 位資料模型 (如果可用)    
    ‐server   選擇 "server" VM 
                  預設 VM 是 server,
                  因為您是在伺服器類計算機上執行。
    ‐cp <目錄和 zip/jar 檔案的類搜尋路徑>
    ‐classpath <目錄和 zip/jar 檔案的類搜尋路徑>
                  用 : 分隔的目錄, JAR 檔案
                  和 ZIP 檔案列表, 用於搜尋類檔案。
    ‐D<名稱>=<值>
                  設定系統屬性
    ‐verbose:[class|gc|jni]
                  啟用詳細輸出
    ‐version      輸出產品版本並退出
    ‐version:<值>
                  警告: 此功能已過時, 將在
                  未來發行版中刪除。
                  需要指定的版本才能執行
    ‐showversion  輸出產品版本並繼續
    ‐jre‐restrict‐search | ‐no‐jre‐restrict‐search
                  警告: 此功能已過時, 將在
                  未來發行版中刪除。
                  在版本搜尋中包括/排除使用者專用 JRE
    ‐? ‐help      輸出此幫助訊息
    ‐X            輸出非標準選項的幫助
    ‐ea[:<packagename>...|:<classname>]
    ‐enableassertions[:<packagename>...|:<classname>]
                  按指定的粒度啟用斷言
    ‐da[:<packagename>...|:<classname>]
    ‐disableassertions[:<packagename>...|:<classname>]
                  禁用具有指定粒度的斷言
    ‐esa | ‐enablesystemassertions
    啟用系統斷言
    ‐dsa | ‐disablesystemassertions
                  禁用系統斷言
    ‐agentlib:<libname>[=<選項>]
                  載入本機代理庫 <libname>, 例如 ‐agentlib:hprof
                  另請參閱 ‐agentlib:jdwp=help 和 ‐agentlib:hprof=help
    ‐agentpath:<pathname>[=<選項>]
                  按完整路徑名載入本機代理庫
    ‐javaagent:<jarpath>[=<選項>]
                  載入 Java 程式語言代理, 請參閱 java.lang.instrument
    ‐splash:<imagepath>
                  使用指定的影象顯示啟動螢幕

3.1、實戰

實戰1:檢視jvm版本

[[email protected] ~]# java ‐version
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141‐b15)
Java HotSpot(TM) 64‐Bit Server VM (build 25.141‐b15, mixed mode)
# ‐showversion引數是表示,先列印版本資訊,再執行後面的命令,在除錯時非常有用,
後面會使用到。

實戰2:通過-D設定系統屬性引數

public class TestJVM {
    public static void main(String[] args) {
        String str = System.getProperty("str");
        if (str == null) {
            System.out.println("testJVM");
        } else {
            System.out.println(str);
        }
    }
}

進行編譯、測試:

#編譯
[[email protected] test]# javac TestJVM.java
#測試
[[email protected] test]# java TestJVM
testJVM
[[email protected] test]# java ‐Dstr=123 TestJVM
123

3.2、-server與-client引數

可以通過-server或-client設定jvm的執行引數。

  • 它們的區別是 Server VM的初始堆空間會大一些,預設使用的是並行垃圾回收器,啟動慢執行快。
  • Client VM 相對來講會保守一些,初始堆空間會小一些,使用序列的垃圾回收器,它的目標是為了讓JVM的啟動速度更快,但執行速度會比Serverm模式慢些。
  • JVM 在啟動的時候會根據硬體和作業系統自動選擇使用Server還是Client型別的JVM。
  • 32 位作業系統
  1. 如果是 Windows系統,不論硬體配置如何,都預設使用Client型別的JVM。
  2. 如果是其他作業系統上,機器配置有 2GB以上的記憶體同時有2個以上CPU的話預設使用server模式,否則使用client模式。
  • 64 位作業系統
  1. 只有 server型別,不支援client型別。

測試:

[[email protected] test]# java ‐client ‐showversion TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141‐b15)
Java HotSpot(TM) 64‐Bit Server VM (build 25.141‐b15, mixed mode)
testJVM
[[email protected] test]# java ‐server ‐showversion TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141‐b15)
Java HotSpot(TM) 64‐Bit Server VM (build 25.141‐b15, mixed mode)
itcast
#由於機器是64位系統,所以不支援client模式

4、-X引數

jvm的-X引數是非標準引數,在不同版本的jvm中,引數可能會有所不同,可以通過java -X檢視非標準引數。

[[email protected] test]# java ‐X
    ‐Xmixed           混合模式執行 (預設)
    ‐Xint             僅解釋模式執行
    ‐Xbootclasspath:<用 : 分隔的目錄和 zip/jar 檔案>
                      設定搜尋路徑以引導類和資源
    ‐Xbootclasspath/a:<用 : 分隔的目錄和 zip/jar 檔案>
                      附加在引導類路徑末尾
    ‐Xbootclasspath/p:<用 : 分隔的目錄和 zip/jar 檔案>
                      置於引導類路徑之前
    ‐Xdiag            顯示附加診斷訊息
    ‐Xnoclassgc       禁用類垃圾收集
    ‐Xincgc           啟用增量垃圾收集
    ‐Xloggc:<file>    將 GC 狀態記錄在檔案中 (帶時間戳)
    ‐Xbatch           禁用後臺編譯
    ‐Xms<size>        設定初始 Java 堆大小
    ‐Xmx<size>        設定最大 Java 堆大小
    ‐Xss<size>        設定 Java 執行緒堆疊大小
    ‐Xprof            輸出 cpu 配置檔案資料
    ‐Xfuture          啟用最嚴格的檢查, 預期將來的預設值
    ‐Xrs              減少 Java/VM 對作業系統訊號的使用 (請參閱文件)
    ‐Xcheck:jni       對 JNI 函式執行其他檢查
    ‐Xshare:off       不嘗試使用共享類資料
    ‐Xshare:auto      在可能的情況下使用共享類資料 (預設)
    ‐Xshare:on        要求使用共享類資料, 否則將失敗。
    ‐XshowSettings    顯示所有設定並繼續
    ‐XshowSettings:all
                      顯示所有設定並繼續
    ‐XshowSettings:vm 顯示所有與 vm 相關的設定並繼續
    ‐XshowSettings:properties
                      顯示所有屬性設定並繼續
    ‐XshowSettings:locale
                      顯示所有與區域設定相關的設定並繼續
‐X 選項是非標準選項, 如有更改, 恕不另行通知。

4.1、-Xint、-Xcomp、-Xmixed

  • 在解釋模式 (interpreted mode)下,-Xint標記會強制JVM執行所有的位元組碼,當然這會降低執行速度,通常低10倍或更多。
  • -Xcomp 引數與它(-Xint)正好相反,JVM在第一次使用時會把所有的位元組碼編譯成原生代碼,從而帶來最大程度的優化。

然而,很多應用在使用 -Xcomp也會有一些效能損失,當然這比使用-Xint損失的少,原因是-xcomp沒有讓JVM啟用JIT編譯器的全部功能。JIT編譯器可以對是否需要編譯做判斷,如果所有程式碼都進行編譯的話,對於一些只執行一次的程式碼就沒有意義了。

  • -Xmixed 是混合模式,將解釋模式與編譯模式進行混合使用,由jvm自己決定,這是jvm預設的模式,也是推薦使用的模式。

示例:強制設定執行模式:

#強制設定為解釋模式
[[email protected] test]# java  ‐showversion ‐Xint TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141‐b15)Java HotSpot(TM) 64‐Bit Server VM (build 25.141‐b15, interpreted mode)
testJVM

#強制設定為編譯模式
[[email protected] test]# java  ‐showversion ‐Xcomp TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141‐b15)
Java HotSpot(TM) 64‐Bit Server VM (build 25.141‐b15, compiled mode)
testJVM
#注意:編譯模式下,第一次執行會比解釋模式下執行慢一些,注意觀察。

#預設的混合模式
[[email protected] test]# java  ‐showversion TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141‐b15)
Java HotSpot(TM) 64‐Bit Server VM (build 25.141‐b15, mixed mode)
testJVM

5、-XX引數

-XX 引數也是非標準引數,主要用於jvm的調優和debug操作。
-XX引數的使用有2種方式,一種是boolean型別,一種是非boolean型別:
boolean 型別:

  • 格式: -XX:[+-]
  • 如: -XX:+DisableExplicitGC 表示禁用手動呼叫gc操作,也就是說呼叫System.gc()無效

非 boolean型別:

  • 格式: -XX:
  • 如: -XX:NewRatio=1 表示新生代和老年代的比值
  • 用法:
[[email protected] test]# java ‐showversion ‐XX:+DisableExplicitGC TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141‐b15)
Java HotSpot(TM) 64‐Bit Server VM (build 25.141‐b15, mixed mode)
testJVM

6、-Xms與-Xmx引數

-Xms與-Xmx分別是設定jvm的堆記憶體的初始大小和最大大小。

  • -Xmx2048m:等價於-XX:MaxHeapSize,設定JVM最大堆記憶體為2048M。
  • -Xms512m:等價於-XX:InitialHeapSize,設定JVM初始堆記憶體為512M。

適當的調整jvm的記憶體大小,可以充分利用伺服器資源,讓程式跑的更快。
示例:

[[email protected] test]# java ‐Xms512m ‐Xmx2048m TestJVM
testJVM

7、檢視jvm的執行引數

有些時候我們需要檢視jvm的執行引數,這個需求可能會存在2種情況:

  • 執行 java命令時打印出執行引數;
  • 檢視正在執行的java程序的引數;

7.1、執行java命令時列印引數

執行java命令時列印引數,需要新增-XX:+PrintFlagsFinal引數即可。

 [[email protected] test]# java ‐XX:+PrintFlagsFinal ‐version
[Global flags]
    uintx AdaptiveSizeDecrementScaleFactor          = 4                  
                {product}
    uintx AdaptiveSizeMajorGCDecayTimeScale         = 10                
                 {product}
    uintx AdaptiveSizePausePolicy                   = 0                  
                {product}
    uintx AdaptiveSizePolicyCollectionCostMargin    = 50                
                 {product}
    uintx AdaptiveSizePolicyInitializingSteps       = 20                
                 {product}
    uintx AdaptiveSizePolicyOutputInterval          = 0                  
                {product}
    uintx AdaptiveSizePolicyWeight                  = 10                
                 {product}
    uintx AdaptiveSizeThroughPutPolicy              = 0                  
                {product}
    uintx AdaptiveTimeWeight                        = 25                
                 {product}
     bool AdjustConcurrency                         = false              
                {product}
     bool AggressiveOpts                            = false              
                {product}
     intx AliasLevel                                = 3                  
                {C2 product}
     bool AlignVector                               = true              
                 {C2 product}
     intx AllocateInstancePrefetchLines             = 1                  
                {product}
     intx AllocatePrefetchDistance                  = 256                
                {product}
     intx AllocatePrefetchInstr                     = 0                  
                {product}
    
     …………………………略…………………………………………
    
   
     bool UseXmmI2D                                 = false              
                {ARCH product}
     bool UseXmmI2F                                 = false              
                {ARCH product}
     bool UseXmmLoadAndClearUpper                   = true              
                 {ARCH product}
     bool UseXmmRegToRegMoveAll                     = true              
                 {ARCH product}
     bool VMThreadHintNoPreempt                     = false              
                {product}
     intx VMThreadPriority                          = ‐1                
                 {product}
     intx VMThreadStackSize                         = 1024              
                 {pd product}
     intx ValueMapInitialSize                       = 11                
                 {C1 product}
     intx ValueMapMaxLoopSize                       = 8                  
                {C1 product}
     intx ValueSearchLimit                          = 1000              
                 {C2 product}
     bool VerifyMergedCPBytecodes                   = true              
                 {product}
     bool VerifySharedSpaces                        = false              
                {product}
     intx WorkAroundNPTLTimedWaitHang               = 1                  
                {product}
    uintx YoungGenerationSizeIncrement              = 20                
                 {product}
    uintx YoungGenerationSizeSupplement             = 80                
                 {product}
    uintx YoungGenerationSizeSupplementDecay        = 8                  
                {product}
    uintx YoungPLABSize                             = 4096              
                 {product}
     bool ZeroTLAB                                  = false              
                {product}
     intx hashCode                                  = 5                  
                {product}
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141‐b15)
Java HotSpot(TM) 64‐Bit Server VM (build 25.141‐b15, mixed mode)

由上述的資訊可以看出,引數有 boolean型別和數字型別,值的操作符是=或:=,分別代表預設值和被修改的值。
示例:

java ‐XX:+PrintFlagsFinal ‐XX:+VerifySharedSpaces ‐version
     intx ValueMapInitialSize                       = 11                
                 {C1 product}
     intx ValueMapMaxLoopSize                       = 8                  
                {C1 product}
     intx ValueSearchLimit                          = 1000              
                 {C2 product}
     bool VerifyMergedCPBytecodes                   = true              
                 {product}
     bool VerifySharedSpaces                       := true              
                 {product}
     intx WorkAroundNPTLTimedWaitHang               = 1                  
                {product}
    uintx YoungGenerationSizeIncrement              = 20                
                 {product}
    uintx YoungGenerationSizeSupplement             = 80                
                 {product}
    uintx YoungGenerationSizeSupplementDecay        = 8                  
                {product}
    uintx YoungPLABSize                             = 4096              
                 {product}
     bool ZeroTLAB                                  = false              
                {product}
     intx hashCode                                  = 5                  
                {product}
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141‐b15)
Java HotSpot(TM) 64‐Bit Server VM (build 25.141‐b15, mixed mode)
#可以看到VerifySharedSpaces這個引數已經被修改了。

7.2、檢視正在執行的jvm引數

如果想要檢視正在執行的jvm就需要藉助於jinfo命令檢視。

首先,啟動一個 tomcat用於測試,來觀察下執行的jvm引數。

 cd /tmp/
 rz 上傳
 tar ‐xvf apache‐tomcat‐7.0.57.tar.gz
 cd apache‐tomcat‐7.0.57
 cd bin/
 ./startup.sh
 #http://192.168.40.133:8080/ 進行訪問

 

#檢視所有的引數,用法:jinfo ‐flags <程序id>
#通過jps 或者  jps ‐l 檢視java程序
[[email protected] bin]# jps
6346 Jps
6219 Bootstrap
[[email protected] bin]# jps ‐l
6358 sun.tools.jps.Jps
6219 org.apache.catalina.startup.Bootstrap
[[email protected] bin]#
[[email protected] bin]# jinfo ‐flags 6219
Attaching to process ID 6219, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.141‐b15
Non‐default VM flags: ‐XX:CICompilerCount=2 ‐XX:InitialHeapSize=31457280
‐XX:MaxHeapSize=488636416 ‐XX:MaxNewSize=162529280 ‐
XX:MinHeapDeltaBytes=524288 ‐XX:NewSize=10485760 ‐XX:OldSize=20971520 ‐
XX:+UseCompressedClassPointers ‐XX:+UseCompressedOops ‐
XX:+UseFastUnorderedTimeStamps ‐XX:+UseParallelGC
Command line:  ‐Djava.util.logging.config.file=/tmp/apache‐tomcat‐
7.0.57/conf/logging.properties ‐
Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager ‐
Djava.endorsed.dirs=/tmp/apache‐tomcat‐7.0.57/endorsed ‐
Dcatalina.base=/tmp/apache‐tomcat‐7.0.57 ‐Dcatalina.home=/tmp/apache‐
tomcat‐7.0.57 ‐Djava.io.tmpdir=/tmp/apache‐tomcat‐7.0.57/temp
#檢視某一引數的值,用法:jinfo ‐flag <引數名> <程序id>
[[email protected] bin]# jinfo ‐flag MaxHeapSize 6219
‐XX:MaxHeapSize=488636416