這幾天線上實例會偶爾報警重啓,查了一下問題,偶然發現了一個java
命令參數順序的坑。html
我線上的啓動命令行爲:java
java -jar app.jar -DcustomArgs=someCustomArgs -Xmx2048M -Xms2048M -XX:MaxMetaspaceSize=256M -XX:MetaspaceSize=256M -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+ParallelRefProcEnabled -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=/log/hs_err_pid%p.log -Xloggc:/log/gc.log -XX:HeapDumpPath=/log
複製代碼
這個命令看起來沒什麼問題,設置了JVM參數,GC日誌打印等等,看起來一切OK。可是,實際上,當線上出現重啓後,我去查看gc日誌,卻發現,壓根沒有gc日誌的文件,我想了想,應當是我設置的參數沒有生效,因而用jps -lvm
查看,結果以下:linux
448 app.jar -DcustomArgs=someCustomArgs -Xmx2048M -Xms2048M -XX:MaxMetaspaceSize=256M -XX:MetaspaceSize=256M -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+ParallelRefProcEnabled -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=/log/hs_err_pid%p.log -Xloggc:/log/gc.log -XX:HeapDumpPath=/log
複製代碼
看起來沒問題啊,因而我又看了看java
命令的操做手冊,發現了問題:編程
java -h
用法: 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>
使用指定的圖像顯示啓動屏幕
有關詳細信息, 請參閱 http://www.oracle.com/technetwork/java/javase/documentation/index.html。
複製代碼
注意這兩點:java [-options] -jar jarfile [args...]
和 -X 輸出非標準選項的幫助
服務器
也就是說,java
這個命令行後邊的[-options]
是在 -jar jarfile
前面的,我那個命令行裏,正好搞反了,個人命令行裏的一堆-X
或者-XX
應當都屬於[-options]
裏面的,這一點可用java -X
驗證:oracle
-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 選項是非標準選項, 若有更改, 恕不另行通知。
複製代碼
也就是說,我由於搞反了java
中參數的順序,致使了java
忽略掉了個人參數。app
那麼,如何看到當前JVM真實應用了的參數呢,我查了一下,發現能夠用jinfo -flags <pid>
或 java -XX:+PrintFlagsFinal
或 java -XX:+PrintCommandLineFlags
來查看。less
運行jinfo -flags <pid>
會有如下輸出:運維
Attaching to process ID 560, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.66-b17
Non-default VM flags: -XX:CICompilerCount=15 -XX:InitialHeapSize=2147483648 -XX:MaxHeapSize=32210157568 -XX:MaxNewSize=10736369664 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=715653120 -XX:OldSize=1431830528 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
Command line:
複製代碼
能夠看到 Non-default
中 MaxHeapSize
和 MaxNewSize
都是大的離譜的數 32G
和 10G
,這兩個數是怎麼來的我暫時沒找到來源點(猜想是運維平臺自動附加的?),同時,能夠看到 Command line:
這裏是空的,也就是沒有應用命令行中的設置參數。jvm
咱們再來運行 java -XX:+PrintCommandLineFlags
,結果以下:
-XX:InitialHeapSize=2147483648 -XX:MaxHeapSize=32210157568 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
複製代碼
能夠看到,在命令行上,應用了參數,參數中設置了MaxHeapSize
和 MaxNewSize
爲大的離譜的數 32G
和 10G
,固然,這個並非我設置的,估計是一些其餘的腳本設置的(猜想,還沒有找到源頭)。
這個算是個不大不小的坑(這個坑和線上問題有沒有關聯還沒有驗證,總之是個坑),之後須要注意。
what's means of jinfo non-default vm flags
感謝 @qixiaobo 的更正,前文提到的那個離譜的32G,應當是在沒有明確指定-Xmx
時,JVM根據策略自動計算出的堆最大的大小,而後那個10G的新生代大小一樣也是依據於策略算出來的,關於其策略可參考官方的Update Release Notes:
還可參考:
As of JDK6U18 following are configurations for the Heap Size.
In the Client JVM, the default Java heap configuration has been modified to improve the performance of today's rich client applications. Initial and maximum heap sizes are larger and settings related to generational garbage collection are better tuned.
The default maximum heap size is half of the physical memory up to a physical memory size of 192 megabytes and otherwise one fourth of the physical memory up to a physical memory size of 1 gigabyte. For example, if your machine has 128 megabytes of physical memory, then the maximum heap size is 64 megabytes, and greater than or equal to 1 gigabyte of physical memory results in a maximum heap size of 256 megabytes. The maximum heap size is not actually used by the JVM unless your program creates enough objects to require it. A much smaller amount, termed the initial heap size, is allocated during JVM initialization. This amount is at least 8 megabytes and otherwise 1/64 of physical memory up to a physical memory size of 1 gigabyte.
Source : www.oracle.com/technetwork…
To answer this question it's critical whether the Java VM is in CLIENT or SERVER mode. You can specify "-client" or "-server" options. Otherwise java uses internal rules; basically win32 is always client and Linux is always server, but see the table here:
Sun/Oracle jre6u18 doc says re client: the VM gets 1/2 of physical memory if machine has <= 192MB; 1/4 of memory if machine has <= 1Gb; max 256Mb. In my test on a 32bit WindowsXP system with 2Gb phys mem, Java allocated 256Mb, which agrees with the doc.
Sun/Oracle jre6u18 doc says re server: same as client, then adds confusing language: for 32bit JVM the default max is 1Gb, and for 64 bit JVM the default is 32Gb. In my test on a 64bit linux machine with 8Gb physical, Java allocates 2Gb, which is 1/4 of physical; on a 64bit linux machine with 128Gb physical Java allocates 32Gb, again 1/4 of physical.
Thanks to this SO post for guiding me:
Definition of server-class machine changed recently?
另外,從JDK源碼hotspot\src\share\vm\runtime\arguments.cpp
也可看到相關邏輯(猜想是這裏,並未看懂)
void Arguments::set_heap_size() {
if (!FLAG_IS_DEFAULT(DefaultMaxRAMFraction)) {
// Deprecated flag
FLAG_SET_CMDLINE(uintx, MaxRAMFraction, DefaultMaxRAMFraction);
}
const julong phys_mem =
FLAG_IS_DEFAULT(MaxRAM) ? MIN2(os::physical_memory(), (julong)MaxRAM)
: (julong)MaxRAM;
// If the maximum heap size has not been set with -Xmx,
// then set it as fraction of the size of physical memory,
// respecting the maximum and minimum sizes of the heap.
if (FLAG_IS_DEFAULT(MaxHeapSize)) {
julong reasonable_max = phys_mem / MaxRAMFraction;
if (phys_mem <= MaxHeapSize * MinRAMFraction) {
// Small physical memory, so use a minimum fraction of it for the heap
reasonable_max = phys_mem / MinRAMFraction;
} else {
// Not-small physical memory, so require a heap at least
// as large as MaxHeapSize
reasonable_max = MAX2(reasonable_max, (julong)MaxHeapSize);
}
if (!FLAG_IS_DEFAULT(ErgoHeapSizeLimit) && ErgoHeapSizeLimit != 0) {
// Limit the heap size to ErgoHeapSizeLimit
reasonable_max = MIN2(reasonable_max, (julong)ErgoHeapSizeLimit);
}
if (UseCompressedOops) {
// Limit the heap size to the maximum possible when using compressed oops
julong max_coop_heap = (julong)max_heap_for_compressed_oops();
if (HeapBaseMinAddress + MaxHeapSize < max_coop_heap) {
// Heap should be above HeapBaseMinAddress to get zero based compressed oops
// but it should be not less than default MaxHeapSize.
max_coop_heap -= HeapBaseMinAddress;
}
reasonable_max = MIN2(reasonable_max, max_coop_heap);
}
reasonable_max = limit_by_allocatable_memory(reasonable_max);
if (!FLAG_IS_DEFAULT(InitialHeapSize)) {
// An initial heap size was specified on the command line,
// so be sure that the maximum size is consistent. Done
// after call to limit_by_allocatable_memory because that
// method might reduce the allocation size.
reasonable_max = MAX2(reasonable_max, (julong)InitialHeapSize);
}
if (PrintGCDetails && Verbose) {
// Cannot use gclog_or_tty yet.
tty->print_cr(" Maximum heap size " SIZE_FORMAT, (size_t) reasonable_max);
}
FLAG_SET_ERGO(uintx, MaxHeapSize, (uintx)reasonable_max);
}
// If the minimum or initial heap_size have not been set or requested to be set
// ergonomically, set them accordingly.
if (InitialHeapSize == 0 || min_heap_size() == 0) {
julong reasonable_minimum = (julong)(OldSize + NewSize);
reasonable_minimum = MIN2(reasonable_minimum, (julong)MaxHeapSize);
reasonable_minimum = limit_by_allocatable_memory(reasonable_minimum);
if (InitialHeapSize == 0) {
julong reasonable_initial = phys_mem / InitialRAMFraction;
reasonable_initial = MAX3(reasonable_initial, reasonable_minimum, (julong)min_heap_size());
reasonable_initial = MIN2(reasonable_initial, (julong)MaxHeapSize);
reasonable_initial = limit_by_allocatable_memory(reasonable_initial);
if (PrintGCDetails && Verbose) {
// Cannot use gclog_or_tty yet.
tty->print_cr(" Initial heap size " SIZE_FORMAT, (uintx)reasonable_initial);
}
FLAG_SET_ERGO(uintx, InitialHeapSize, (uintx)reasonable_initial);
}
// If the minimum heap size has not been set (via -Xms),
// synchronize with InitialHeapSize to avoid errors with the default value.
if (min_heap_size() == 0) {
set_min_heap_size(MIN2((uintx)reasonable_minimum, InitialHeapSize));
if (PrintGCDetails && Verbose) {
// Cannot use gclog_or_tty yet.
tty->print_cr(" Minimum heap size " SIZE_FORMAT, min_heap_size());
}
}
}
}
複製代碼