JVM参数调整,经验分享,转载自撒迦
0、各參數(shù)的默認值 在討論HotSpot VM的各參數(shù)的陷阱前,大家應(yīng)該先了解HotSpot VM到底有哪些參數(shù)可以設(shè)置,這些參數(shù)的默認值都是什么。 有幾種辦法可以幫助大家獲取參數(shù)的信息。首先為了大致了解都有些什么參數(shù)可以設(shè)置,可以參考HotSpot VM里的各個globals.hpp文件:
globals.hpp?
globals_extension.hpp?
c1_globals.hpp?
c1_globals_linux.hpp?
c1_globals_solaris.hpp?
c1_globals_sparc.hpp?
c1_globals_windows.hpp?
c1_globals_x86.hpp?
c2_globals.hpp?
c2_globals_linux.hpp?
c2_globals_solaris.hpp?
c2_globals_sparc.hpp?
c2_globals_windows.hpp?
c2_globals_x86.hpp?
g1_globals.hpp?
globals_linux.hpp?
globals_linux_sparc.hpp?
globals_linux_x86.hpp?
globals_linux_zero.hpp?
globals_solaris.hpp?
globals_solaris_sparc.hpp?
globals_solaris_x86.hpp?
globals_sparc.hpp?
globals_windows.hpp?
globals_windows_x86.hpp?
globals_x86.hpp?
globals_zero.hpp?
shark_globals.hpp?
shark_globals_zero.hpp?
arguments.cpp?
然后是 -XX:+PrintCommandLineFlags 。這個參數(shù)的作用是顯示出VM初始化完畢后所有跟最初的默認值不同的參數(shù)及它們的值。 這個參數(shù)至少在Sun JDK 5上已經(jīng)開始支持,Oracle/Sun JDK 6以及Oracle JDK 7上也可以使用。Sun JDK 1.4.2還不支持這個參數(shù)。 例子: Command prompt代碼 收藏代碼$ java -XX:+PrintCommandLineFlags VM option '+PrintCommandLineFlags' -XX:InitialHeapSize=57344000 -XX:MaxHeapSize=917504000 -XX:ParallelGCThreads=4 -XX:+PrintCommandLineFlags -XX:+UseCompressedOops -XX:+UseParallelGC 《Java Performance》一書主要是用這個參數(shù)來介紹HotSpot VM各參數(shù)的效果的。 接著是 -XX:+PrintFlagsFinal 。前一個參數(shù)只顯示跟默認值不同的,而這個參數(shù)則可以顯示所有可設(shè)置的參數(shù)及它們的值。不過這個參數(shù)本身只從JDK 6 update 21開始才可以用,之前的Oracle/Sun JDK則用不了。 可以設(shè)置的參數(shù)默認是不包括diagnostic或experimental系的。要在-XX:+PrintFlagsFinal的輸出里看到這兩種參數(shù)的信息,分別需要顯式指定-XX:+UnlockDiagnosticVMOptions / -XX:+UnlockExperimentalVMOptions 。 再下來,-XX:+PrintFlagsInitial 。這個參數(shù)顯示在處理參數(shù)之前所有可設(shè)置的參數(shù)及它們的值,然后直接退出程序。“參數(shù)處理”包括許多步驟,例如說檢查參數(shù)之間是否有沖突,通過ergonomics調(diào)整某些參數(shù)的值,之類的。 結(jié)合-XX:+PrintFlagsInitial與-XX:+PrintFlagsFinal,對比兩者的差異,就可以知道ergonomics對哪些參數(shù)做了怎樣的調(diào)整。 這兩個參數(shù)的例子: Command prompt代碼 收藏代碼$ java -version java version "1.6.0_29" Java(TM) SE Runtime Environment (build 1.6.0_29-b11) Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02, mixed mode) $ java -XX:+PrintFlagsInitial | grep UseCompressedOops bool UseCompressedOops = false {lp64_product} $ java -XX:+PrintFlagsFinal | grep UseCompressedOops bool UseCompressedOops := true {lp64_product} Oracle/Sun JDK 6從update 23開始會由ergonomics在合適的條件下默認啟用壓縮指針功能。這個例子演示的是UseCompressedOops的初始默認值是false,由PrintFlagsInitial的輸出可以看到;然后經(jīng)過ergonomics自動調(diào)整后,最終采用的默認值是true,由PrintFlagsFinal的輸出可以看到。 輸出里“=”表示使用的是初始默認值,而“:=”表示使用的不是初始默認值,可能是命令行傳進來的參數(shù)、配置文件里的參數(shù)或者是ergonomics自動選擇了別的值。 除了在VM啟動時傳些特殊的參數(shù)讓它打印出自己的各參數(shù)外,jinfo -flag 可以用來查看某個參數(shù)的值,也可以用來設(shè)定manageable系參數(shù)的值。請參考這帖的例子:通過jinfo工具在full GC前后做heap dump 之前發(fā)過某些環(huán)境中HotSpot VM的各參數(shù)的默認值,可以參考一下。?
======================================================================?
1、-XX:+DisableExplicitGC 與 NIO的direct memory 很多人都見過JVM調(diào)優(yōu)建議里使用這個參數(shù),對吧?但是為什么要用它,什么時候應(yīng)該用而什么時候用了會掉坑里呢? 首先要了解的是這個參數(shù)的作用。在Oracle/Sun JDK這個具體實現(xiàn)上,System.gc()的默認效果是引發(fā)一次stop-the-world的full GC,對整個GC堆做收集。有幾個參數(shù)可以改變默認行為,之前發(fā)過一帖簡單描述過,這里就不重復(fù)了。關(guān)鍵點是,用了-XX:+DisableExplicitGC參數(shù)后,System.gc()的調(diào)用就會變成一個空調(diào)用,完全不會觸發(fā)任何GC(但是“函數(shù)調(diào)用”本身的開銷還是存在的哦~)。 為啥要用這個參數(shù)呢?最主要的原因是為了防止某些手賤的同學(xué)在代碼里到處寫System.gc()的調(diào)用而干擾了程序的正常運行吧。有些應(yīng)用程序本來可能正常跑一天也不會出一次full GC,但就是因為有人在代碼里調(diào)用了System.gc()而不得不間歇性被暫停。也有些時候這些調(diào)用是在某些庫或框架里寫的,改不了它們的代碼但又不想被這些調(diào)用干擾也會用這參數(shù)。 OK。看起來這參數(shù)應(yīng)該總是開著嘛。有啥坑呢? 其中一種情況是下述三個條件同時滿足時會發(fā)生的: 1、應(yīng)用本身在GC堆內(nèi)的對象行為良好,正常情況下很久都不發(fā)生full GC;?
2、應(yīng)用大量使用了NIO的direct memory,經(jīng)常、反復(fù)的申請DirectByteBuffer?
3、使用了-XX:+DisableExplicitGC 能觀察到的現(xiàn)象是: Log代碼 收藏代碼java.lang.OutOfMemoryError: Direct buffer memory at java.nio.Bits.reserveMemory(Bits.java:633) at java.nio.DirectByteBuffer.(DirectByteBuffer.java:98) at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288) ... 做個簡單的例子來演示這現(xiàn)象: Java代碼 收藏代碼import java.nio.*; public class DisableExplicitGCDemo { public static void main(String[] args) { for (int i = 0; i < 100000; i++) { ByteBuffer.allocateDirect(128); } System.out.println("Done"); } } 然后編譯、運行之: Command prompt代碼 收藏代碼$ java -version java version "1.6.0_25" Java(TM) SE Runtime Environment (build 1.6.0_25-b06) Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode) $ javac DisableExplicitGCDemo.java $ java -XX:MaxDirectMemorySize=10m -XX:+PrintGC -XX:+DisableExplicitGC DisableExplicitGCDemo Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory at java.nio.Bits.reserveMemory(Bits.java:633) at java.nio.DirectByteBuffer.(DirectByteBuffer.java:98) at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288) at DisableExplicitGCDemo.main(DisableExplicitGCDemo.java:6) $ java -XX:MaxDirectMemorySize=10m -XX:+PrintGC DisableExplicitGCDemo [GC 10996K->10480K(120704K), 0.0433980 secs] [Full GC 10480K->10415K(120704K), 0.0359420 secs] Done 可以看到,同樣的程序,不帶-XX:+DisableExplicitGC時能正常完成運行,而帶上這個參數(shù)后卻出現(xiàn)了OOM。 例子里用-XX:MaxDirectMemorySize=10m限制了DirectByteBuffer能分配的空間的限額,以便問題更容易展現(xiàn)出來。不用這個參數(shù)就得多跑一會兒了。 在這個例子里,main()里的循環(huán)不斷申請DirectByteBuffer但并沒有引用、使用它們,所以這些DirectByteBuffer應(yīng)該剛創(chuàng)建出來就已經(jīng)滿足被GC的條件,等下次GC運行的時候就應(yīng)該可以被回收。 實際上卻沒這么簡單。DirectByteBuffer是種典型的“冰山”對象,也就是說它的Java對象雖然很小很無辜,但它背后卻會關(guān)聯(lián)著一定量的native memory資源,而這些資源并不在GC的控制之下,需要自己注意控制好。對JVM如何使用native memory不熟悉的同學(xué)可以參考去年JavaOne上IBM的一個演講,“Where Does All the Native Memory Go”。 Oracle/Sun JDK的實現(xiàn)里,DirectByteBuffer有幾處值得注意的地方。
1、DirectByteBuffer沒有finalizer,它的native memory的清理工作是通過sun.misc.Cleaner自動完成的。 2、sun.misc.Cleaner是一種基于PhantomReference的清理工具,比普通的finalizer輕量些。對PhantomReference不熟悉的同學(xué)請參考Bob Lee最近幾年在JavaOne上做的演講,"The Ghost in the Virtual Machine: A Reference to References"。今年的JavaOne上他也講了同一個主題,內(nèi)容比前幾年的稍微更新了些。PPT可以從鏈接里的頁面下載到。 Java代碼 收藏代碼/** * General-purpose phantom-reference-based cleaners. * *
Cleaners are a lightweight and more robust alternative to finalization. * They are lightweight because they are not created by the VM and thus do not * require a JNI upcall to be created, and because their cleanup code is * invoked directly by the reference-handler thread rather than by the * finalizer thread. They are more robust because they use phantom references, * the weakest type of reference object, thereby avoiding the nasty ordering * problems inherent to finalization. * *
A cleaner tracks a referent object and encapsulates a thunk of arbitrary * cleanup code. Some time after the GC detects that a cleaner's referent has * become phantom-reachable, the reference-handler thread will run the cleaner. * Cleaners may also be invoked directly; they are thread safe and ensure that * they run their thunks at most once. * *
Cleaners are not a replacement for finalization. They should be used * only when the cleanup code is extremely simple and straightforward. * Nontrivial cleaners are inadvisable since they risk blocking the * reference-handler thread and delaying further cleanup and finalization. * * * @author Mark Reinhold * @version %I%, %E% */ 重點是這兩句:"A cleaner tracks a referent object and encapsulates a thunk of arbitrary cleanup code. Some time after the GC detects that a cleaner's referent has become phantom-reachable, the reference-handler thread will run the cleaner." Oracle/Sun JDK 6中的HotSpot VM只會在old gen GC(full GC/major GC或者concurrent GC都算)的時候才會對old gen中的對象做reference processing,而在young GC/minor GC時只會對young gen里的對象做reference processing。 (死在young gen中的DirectByteBuffer對象會在young GC時被處理的例子,請參考這里:https://gist.github.com/1614952) 也就是說,做full GC的話會對old gen做reference processing,進而能觸發(fā)Cleaner對已死的DirectByteBuffer對象做清理工作。而如果很長一段時間里沒做過GC或者只做了young GC的話則不會在old gen觸發(fā)Cleaner的工作,那么就可能讓本來已經(jīng)死了的、但已經(jīng)晉升到old gen的DirectByteBuffer關(guān)聯(lián)的native memory得不到及時釋放。 3、為DirectByteBuffer分配空間過程中會顯式調(diào)用System.gc(),以期通過full GC來強迫已經(jīng)無用的DirectByteBuffer對象釋放掉它們關(guān)聯(lián)的native memory: Java代碼 收藏代碼// These methods should be called whenever direct memory is allocated or // freed. They allow the user to control the amount of direct memory // which a process may access. All sizes are specified in bytes. static void reserveMemory(long size) { synchronized (Bits.class) { if (!memoryLimitSet && VM.isBooted()) { maxMemory = VM.maxDirectMemory(); memoryLimitSet = true; } if (size <= maxMemory - reservedMemory) { reservedMemory += size; return; } } System.gc(); try { Thread.sleep(100); } catch (InterruptedException x) { // Restore interrupt status Thread.currentThread().interrupt(); } synchronized (Bits.class) { if (reservedMemory + size > maxMemory) throw new OutOfMemoryError("Direct buffer memory"); reservedMemory += size; } } 這幾個實現(xiàn)特征使得Oracle/Sun JDK 6依賴于System.gc()觸發(fā)GC來保證DirectByteMemory的清理工作能及時完成。如果打開了-XX:+DisableExplicitGC,清理工作就可能得不到及時完成,于是就有機會見到direct memory的OOM,也就是上面的例子演示的情況。我們這邊在實際生產(chǎn)環(huán)境中確實遇到過這樣的問題。 教訓(xùn)是:如果你在使用Oracle/Sun JDK 6,應(yīng)用里有任何地方用了direct memory,那么使用-XX:+DisableExplicitGC要小心。如果用了該參數(shù)而且遇到direct memory的OOM,可以嘗試去掉該參數(shù)看是否能避開這種OOM。如果擔(dān)心System.gc()調(diào)用造成full GC頻繁,可以嘗試下面提到 -XX:+ExplicitGCInvokesConcurrent 參數(shù) ======================================================================
?2、-XX:+DisableExplicitGC 與 Remote Method Invocation (RMI) 與 -Dsun.rmi.dgc.{server|client}.gcInterval= 看了上一個例子有沒有覺得-XX:+DisableExplicitGC參數(shù)用起來很危險?那干脆完全不要用這個參數(shù)吧。又有什么坑呢? 前段時間有個應(yīng)用的開發(fā)來抱怨,說某次升級JDK之前那應(yīng)用的GC狀況都很好,很長時間都不會發(fā)生full GC,但升級后發(fā)現(xiàn)每一小時左右就會發(fā)生一次。經(jīng)過對比發(fā)現(xiàn),升級的同時也吧啟動參數(shù)改了,把原本有的-XX:+DisableExplicitGC給去掉了。 觀察到的日志有明顯特征。一位同事表示: 引用線上機器出現(xiàn)一個場景;每隔1小時出現(xiàn)一次Full GC,用btrace看了一下調(diào)用地: who call system.gc : sun.misc.GC$Daemon.run(GC.java:92) 預(yù)發(fā)機沒什么流量,也會每一小時一次Full GC 頻率正好是一小時一次 Gc log代碼 收藏代碼2011-09-23T10:49:38.071+0800: 327692.227: [Full GC (System) 327692.227: [CMS: 75793K->75759K(2097152K), 0.6923690 secs] 430298K->75759K(3984640K), [CMS Perm : 104136K->104124K(173932K)], 0.6925570 secs] 實際上這里在做的是分布式GC。Sun JDK的分布式GC是用純Java實現(xiàn)的,為RMI服務(wù)。 RMI DGC相關(guān)參數(shù)的介紹文檔:http://docs.oracle.com/javase/6/docs/technotes/guides/rmi/sunrmiproperties.html (后面回頭再補…先睡覺去了) 資料: jGuru: Distributed Garbage Collection http://mail.openjdk.java.net/pipermail/hotspot-dev/2011-December/004909.html Tony Printezis 寫道RMI has a distributed GC that relies on reference processing to allow each node to recognize that some objects are unreachable so it can notify a remote node (or nodes) that some remote references to them do not exist any more. The remote node might then be able to reclaim objects that are only remotely reachable. (Or this is how I understood it at least.) RMI used to call System.gc() once a minute (!!!) but after some encouragement from yours truly they changed the default to once an hour (this is configurable using a property). Note that a STW Full GC is not really required as long as references are processed. So, in CMS (and G1), a concurrent cycle is fine which is why we recommend to use -XX:+ExplicitGCInvokesConcurrent in this case. I had been warned by the RMI folks against totally disabling those System.gc()'s (e.g., using -XX:+DisableExplicitGC) given that if Full GCs / concurrent cycles do not otherwise happen at a reasonable frequency then remote nodes might experience memory leaks since they will consider that some otherwise unreachable remote references are still live. I have no idea how severe such memory leaks would be. I guess they'd be very application-dependent. An additional thought that just occurred to me: instead of calling System.gc() every hour what RMI should really be doing is calling System.gc() every hour provided no old gen GC has taken place during the last hour. This would be relatively easy to implement by accessing the old GC counter through the GC MXBeans. Tony再加倆鏈接:http://mail.openjdk.java.net/pipermail/hotspot-dev/2012-January/004929.html http://mail.openjdk.java.net/pipermail/hotspot-dev/2012-January/004946.html 《Java Performance》的303和411頁正好也提到了-XX:+DisableExplicitGC與RMI之間的干擾的事情,有興趣可以讀一下,雖然只有一小段。 ======================================================================?
3、-XX:+ExplicitGCInvokesConcurrent 或 -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses C++代碼 收藏代碼product(bool, ExplicitGCInvokesConcurrent, false, \ "A System.gc() request invokes a concurrent collection;" \ " (effective only when UseConcMarkSweepGC)") \ \ product(bool, ExplicitGCInvokesConcurrentAndUnloadsClasses, false, \ "A System.gc() request invokes a concurrent collection and " \ "also unloads classes during such a concurrent gc cycle " \ "(effective only when UseConcMarkSweepGC)") \ 跟上面的第一個例子的-XX:+DisableExplicitGC一樣,這兩個參數(shù)也是用來改變System.gc()的默認行為用的;不同的是這兩個參數(shù)只能配合CMS使用(-XX:+UseConcMarkSweepGC),而且System.gc()還是會觸發(fā)GC的,只不過不是觸發(fā)一個完全stop-the-world的full GC,而是一次并發(fā)GC周期。 CMS GC周期中也會做reference processing。所以如果用這兩個參數(shù)的其中一個,而不是用-XX:+DisableExplicitGC的話,就避開了由full GC帶來的長GC pause,同時NIO direct memory的OOM也不會那么容易發(fā)生。 做了個跟第一個例子類似的例子,在這里:https://gist.github.com/1344251 《Java Performance》的303頁有講到這倆參數(shù)。 相關(guān)bug:6919638 CMS: ExplicitGCInvokesConcurrent misinteracts with gc locker << JDK6u23修復(fù)了這個問題?
======================================================================
?4、-XX:+GCLockerInvokesConcurrent C++代碼 收藏代碼product(bool, GCLockerInvokesConcurrent, false, \ "The exit of a JNI CS necessitating a scavenge also" \ " kicks off a bkgrd concurrent collection") \ (內(nèi)容回頭補…)
?======================================================================?
5、MaxDirectMemorySize 與 NIO direct memory 的默認上限 -XX:MaxDirectMemorySize 是用來配置NIO direct memory上限用的VM參數(shù)。 C++代碼 收藏代碼product(intx, MaxDirectMemorySize, -1, \ "Maximum total size of NIO direct-buffer allocations") \ 但如果不配置它的話,direct memory默認最多能申請多少內(nèi)存呢?這個參數(shù)默認值是-1,顯然不是一個“有效值”。所以真正的默認值肯定是從別的地方來的。 在Sun JDK 6和OpenJDK 6里,有這樣一段代碼,sun.misc.VM: Java代碼 收藏代碼// A user-settable upper limit on the maximum amount of allocatable direct // buffer memory. This value may be changed during VM initialization if // "java" is launched with "-XX:MaxDirectMemorySize=". // // The initial value of this field is arbitrary; during JRE initialization // it will be reset to the value specified on the command line, if any, // otherwise to Runtime.getRuntime().maxMemory(). // private static long directMemory = 64 * 1024 * 1024; // If this method is invoked during VM initialization, it initializes the // maximum amount of allocatable direct buffer memory (in bytes) from the // system property sun.nio.MaxDirectMemorySize. The system property will // be removed when it is accessed. // // If this method is invoked after the VM is booted, it returns the // maximum amount of allocatable direct buffer memory. // public static long maxDirectMemory() { if (booted) return directMemory; Properties p = System.getProperties(); String s = (String)p.remove("sun.nio.MaxDirectMemorySize"); System.setProperties(p); if (s != null) { if (s.equals("-1")) { // -XX:MaxDirectMemorySize not given, take default directMemory = Runtime.getRuntime().maxMemory(); } else { long l = Long.parseLong(s); if (l > -1) directMemory = l; } } return directMemory; } (代碼里原本的注釋有個寫錯的地方,上面有修正) 當(dāng)MaxDirectMemorySize參數(shù)沒被顯式設(shè)置時它的值就是-1,在Java類庫初始化時maxDirectMemory()被java.lang.System的靜態(tài)構(gòu)造器調(diào)用,走的路徑就是這條: Java代碼 收藏代碼if (s.equals("-1")) { // -XX:MaxDirectMemorySize not given, take default directMemory = Runtime.getRuntime().maxMemory(); } 而Runtime.maxMemory()在HotSpot VM里的實現(xiàn)是: C++代碼 收藏代碼JVM_ENTRY_NO_ENV(jlong, JVM_MaxMemory(void)) JVMWrapper("JVM_MaxMemory"); size_t n = Universe::heap()->max_capacity(); return convert_size_t_to_jlong(n); JVM_END 這個max_capacity()實際返回的是 -Xmx減去一個survivor space的預(yù)留大小(G1除外)。 結(jié)論:MaxDirectMemorySize沒顯式配置的時候,NIO direct memory可申請的空間的上限就是-Xmx減去一個survivor space的預(yù)留大小。 大家感興趣的話可以試試在不同的-Xmx的條件下不設(shè)置MaxDirectMemorySize,并且調(diào)用一下sun.misc.VM.maxDirectMemory()看得到的值的相關(guān)性。 該行為在JDK7里沒變,雖然具體實現(xiàn)的代碼有些變化。請參考http://hg.openjdk.java.net/jdk7/jdk7/jdk/rev/b444f86c4abe ======================================================================
?6、-verbose:gc 與 -XX:+PrintGCDetails 經(jīng)常能看到在推薦的標(biāo)準(zhǔn)參數(shù)里這兩個參數(shù)一起出現(xiàn)。實際上它們有啥關(guān)系? 在Oracle/Sun JDK 6里,"java"這個啟動程序遇到"-verbosegc"會將其轉(zhuǎn)換為"-verbose:gc",將啟動參數(shù)傳給HotSpot VM后,HotSpot VM遇到"-verbose:gc"則會當(dāng)作"-XX:+PrintGC"來處理。 也就是說 -verbosegc、-verbose:gc、-XX:+PrintGC 三者的作用是完全一樣的。 而當(dāng)HotSpot VM遇到 -XX:+PrintGCDetails 參數(shù)時,會順帶把 -XX:+PrintGC 給設(shè)置上。 也就是說 -XX:+PrintGCDetails 包含 -XX:+PrintGC,進而也就包含 -verbose:gc。 既然 -verbose:gc 都被包含了,何必在命令行參數(shù)里顯式設(shè)置它呢? ======================================================================?
7、-XX:+UseFastEmptyMethods 與 -XX:+UseFastAccessorMethods 雖然不常見,但偶爾也會見到推薦的標(biāo)準(zhǔn)參數(shù)上有這倆的身影。 empty method顧名思義就是空方法,也就是方法體只包含一條return指令、返回值類型為void的Java方法。 accessor method在這里則有很具體的定義: C++代碼 收藏代碼bool methodOopDesc::is_accessor() const { if (code_size() != 5) return false; if (size_of_parameters() != 1) return false; if (java_code_at(0) != Bytecodes::_aload_0 ) return false; if (java_code_at(1) != Bytecodes::_getfield) return false; if (java_code_at(4) != Bytecodes::_areturn && java_code_at(4) != Bytecodes::_ireturn ) return false; return true; } 如果從Java源碼的角度來理解,accessor method就是形如這樣的: Java代碼 收藏代碼public class Foo { private int value; public int getValue() { return this.value; } } 關(guān)鍵點是: 1、必須是成員方法;靜態(tài)方法不行 2、返回值類型必須是引用類型或者int,其它都不算 3、方法體的代碼必須滿足aload_0; getfield #index; areturn或ireturn這樣的模式。 留意:方法名是什么都沒關(guān)系,是不是get、is、has開頭都不重要。 那么這倆有啥問題? 取自JDK 6 update 27: C++代碼 收藏代碼product(bool, UseFastEmptyMethods, true, \ "Use fast method entry code for empty methods") \ \ product(bool, UseFastAccessorMethods, true, \ "Use fast method entry code for accessor methods") \ 看到這倆參數(shù)的默認值都是true了么?也就是說,在Oracle/Sun JDK 6上設(shè)置這參數(shù)其實也是沒意義的,跟默認一樣,一直到最新的JDK 6 update 29都是如此。 不過在Oracle/Sun JDK 7里,情況有變化。 Bug ID: 6385687 UseFastEmptyMethods/UseFastAccessorMethods considered harmful 在上述bug對應(yīng)的代碼變更后,這倆參數(shù)的默認值改為了false。 本來想多寫點這塊的…算,還是長話短說。 Oracle JDK 7里的HotSpot VM已經(jīng)開始有比較好的多層編譯(tiered compilation)支持,可以預(yù)見在不久的將來該模式將成為HotSpot VM默認的執(zhí)行模式。當(dāng)前該模式尚未默認開啟;可以通過 -XX:+TieredCompilation 來開啟。 有趣的是,在使用多層編譯模式時,如果UseFastAccessorMethods/UseFastEmptyMethods是開著的,有些多態(tài)方法調(diào)用點的性能反而會顯著下降。所以,為了適應(yīng)多層編譯模式,JDK 7里這兩個參數(shù)的默認值就被改為false了。 在郵件列表上有過相關(guān)討論:review for 6385687: UseFastEmptyMethods/UseFastAccessorMethods considered harmful ======================================================================?
8、-XX:+UseCMSCompactAtFullCollection 這個參數(shù)在Oracle/Sun JDK 6里一直都默認是true,完全沒必要顯式設(shè)置,設(shè)了也不會有啥不同的效果。 C++代碼 收藏代碼product(bool, UseCMSCompactAtFullCollection, true, \ "Use mark sweep compact at full collections") \ 我不認為顯式設(shè)置一個跟默認值相同的參數(shù)有什么維護上的好處。要維護的參數(shù)多了反而更容易成為維護的噩夢吧。后面的人會不知道到底當(dāng)初為什么要設(shè)置這個參數(shù)。 相關(guān)的有個 CMSFullGCsBeforeCompaction 參數(shù),請參考另一帖里的討論:http://hllvm.group.iteye.com/group/topic/28854#209294 同樣,在Oracle/Sun JDK 6和OpenJDK 6里,CMSParallelRemarkEnabled 也一直默認是true,沒必要顯式設(shè)置-XX:+CMSParallelRemarkEnabled。 有很多bool類型的參數(shù)默認都是true,顯式設(shè)置它們之前最好先用這帖開頭介紹的辦法看看默認值是否已經(jīng)是想要的值了。 C++代碼 收藏代碼product(bool, CMSScavengeBeforeRemark, false, \ "Attempt scavenge before the CMS remark step") \ 這個默認倒是false。如果一個應(yīng)用統(tǒng)計到的young GC時間都比較短而CMS remark的時間比較長,那么可以試試打開這個參數(shù),在做remark之前先做一次young GC。是否能有效縮短remark的時間視應(yīng)用情況而異,所以開這個參數(shù)的話請一定做好測試。
?======================================================================
?9、-XX:CMSMaxAbortablePrecleanTime=5000 同上…默認就是5000 C++代碼 收藏代碼product(intx, CMSMaxAbortablePrecleanTime, 5000, \ "(Temporary, subject to experimentation)" \ "Maximum time in abortable preclean in ms") \ 還是不要設(shè)跟默認值一樣的參數(shù)了吧。 ======================================================================?
10、-Xss 與 -XX:ThreadStackSize 參考我之前發(fā)過的兩帖: What the difference between -Xss and -XX:ThreadStackSize is? Inconsistency between -Xss and -XX:ThreadStackSize in the java launcher (詳情回頭補~)
?======================================================================?
11、-Xmn 與 -XX:NewSize、-XX:MaxNewSize 如果同時設(shè)置了-XX:NewSize與-XX:MaxNewSize遇到“Could not reserve enough space for object heap”錯誤的話,請看看是不是這帖所說的問題。早期JDK 6似乎都受這問題影響,一直到JDK 6 update 14才修復(fù)。 ======================================================================?
12、-Xmn 與 -XX:NewRatio ======================================================================?
13、-XX:NewRatio 與 -XX:NewSize、-XX:OldSize ======================================================================?
14、jmap -heap看到的參數(shù)值與實際起作用的參數(shù)的關(guān)系? 發(fā)了幾個例子在這里:https://gist.github.com/1363195 其中有個看起來很恐怖的值: Java代碼 收藏代碼MaxNewSize = 17592186044415 MB 這是啥來的? C++代碼 收藏代碼product(uintx, MaxNewSize, max_uintx, \ "Maximum new generation size (in bytes), max_uintx means set " \ "ergonomically") 在HotSpot VM里,intx是跟平臺字長一樣寬的帶符號整型,uintx是其無符號版。 max_uintx是(uintx) -1,也就是說在32位平臺上是無符號的0xFFFFFFFF,64位平臺上則是0xFFFFFFFFFFFFFFFF。 jmap -heap顯示的部分參數(shù)是以MB為單位來顯示的,而MaxNewSize的單位是byte。我跑例子的平臺是64位的,于是算一下 0xFFFFFFFFFFFFFFFF / 1024 / 1024 = 17592186044415 MB 。 參數(shù)的說明告訴我們,當(dāng)MaxNewSize的值等于max_uintx時,意思就是交由ergonomics來自動選擇young gen的最大大小。并不是說young gen的最大大小真的有0xFFFFFFFFFFFFFFFF這么大。 要注意的是,HotSpot VM有大量可調(diào)節(jié)的參數(shù),并不是所有參數(shù)在某次運行的時候都有效。 例如說設(shè)置了-Xmn的話,NewRatio就沒作用了。 又例如說, C++代碼 收藏代碼product(uintx, OldSize, ScaleForWordSize(4*M), \ "Initial tenured generation size (in bytes)") \ -XX:OldSize參數(shù)的默認值在32位平臺上是4M,在64位平臺上是5M多。但如果這個參數(shù)沒有被顯式設(shè)置過,那它實際上是沒作用的;old gen的大小會通過Java heap的整體大小與young gen的大小配置計算出來,但OldSize參數(shù)卻沒有被更新(因為根本沒用它)。于是這個參數(shù)的值與實際運行的狀況就可能會不相符。 一種例外的情況是,如果-Xmx非常小,比NewSize+OldSize的默認值還小,那這個OldSize的默認值就會起作用,把MaxHeapSize給撐大。 C++代碼 收藏代碼void TwoGenerationCollectorPolicy::initialize_flags() { GenCollectorPolicy::initialize_flags(); OldSize = align_size_down(OldSize, min_alignment()); if (NewSize + OldSize > MaxHeapSize) { MaxHeapSize = NewSize + OldSize; } MaxHeapSize = align_size_up(MaxHeapSize, max_alignment()); //... } 可以看這邊的一個例子:https://gist.github.com/1375782
?======================================================================?
15、-XX:+AlwaysTenure、-XX:+NeverTenure、-XX:MaxTenuringThreshold=0 或 "-XX:MaxTenuringThreshold=markOopDesc::max_age + 1" ParNew的時候,設(shè)定-XX:+AlwaysTenure隱含-XX:MaxTenuringThreshold=0;不過-XX:+NeverTenure卻沒啥特別的作用。 -XX:+AlwaysTenure 與 -XX:+NeverTenure 是互斥的,最后一個出現(xiàn)的那個會同時決定這兩個參數(shù)的值。
?======================================================================
?16、-XX:MaxTenuringThreshold 的默認值? C++代碼 收藏代碼product(intx, MaxTenuringThreshold, 15, \ "Maximum value for tenuring threshold") \ Oracle/Sun JDK 6中,選擇CMS之外的GC時,MaxTenuringThreshold(以下簡稱MTT)的默認值是15;而選擇了CMS的時候,MTT的默認值是4而不是15。設(shè)定是在 Arguments::set_cms_and_parnew_gc_flags() 里做的。 在Sun JDK 6之前(1.4.2、5),選擇CMS的時候MTT的默認值則是0,也就是等于設(shè)定了-XX:+AlwaysTenure——所有eden里的活對象在經(jīng)歷第一次minor GC的時候就會直接晉升到old gen,而survivor space直接就沒用了。 Command prompt代碼 收藏代碼$ java -version java version "1.6.0_25" Java(TM) SE Runtime Environment (build 1.6.0_25-b06) Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode) $ java -XX:+PrintFlagsFinal | egrep 'MaxTenuringThreshold|UseParallelGC|UseConcMarkSweepGC' intx MaxTenuringThreshold = 15 {product} bool UseConcMarkSweepGC = false {product} bool UseParallelGC := true {product} $ java -XX:+PrintFlagsFinal -XX:+UseConcMarkSweepGC | egrep 'MaxTenuringThreshold|UseParallelGC|UseConcMarkSweepGC' intx MaxTenuringThreshold := 4 {product} bool UseConcMarkSweepGC := true {product} bool UseParallelGC = false {product} ======================================================================?
17、-XX:+CMSClassUnloadingEnabled CMS remark暫停時間會增加,所以如果類加載并不頻繁、String的intern也沒有大量使用的話,這個參數(shù)還是不開的好。 ======================================================================?
18、-XX:+AggressiveHeap ======================================================================?
19、-XX:+UseCompressedOops 有益?有害? 先把微博上回復(fù)別人問題的解答放這邊。 本來如果功能沒bug的話,Oracle/Sun JDK 6的64位HotSpot上,GC堆在26G以下(-Xmx + -XX:MaxPermSize)的時候用多數(shù)都是有益的。 開啟壓縮指針后,從代碼路徑(code path)和CPI(cycles per instruction)兩個角度看,情況是不一樣的: ·開啟壓縮指針會使代碼路徑變長,因為所有在GC堆里的、指向GC堆內(nèi)對象的指針都會被壓縮,這些指針的訪問就需要更多的代碼才可以實現(xiàn)。不要以為只是讀寫字段才受影響,其實實例方法調(diào)用、子類型檢查等操作也受影響——“klass”也是一個指針,也被壓縮了。 ·但從CPI的角度看,由于壓縮指針使需要拷貝的數(shù)據(jù)量變小了,cache miss的幾率隨之降低,結(jié)果CPI可能會比壓縮前降低。綜合來看,開了壓縮指針通常能大幅降低GC堆內(nèi)存的消耗,同時維持或略提高Java程序的速度。 但,JDK6u23之前那個參數(shù)的bug實在太多,最好別用;而6u23之后它就由ergonomics自動開啟了,不用自己設(shè)。如果在6u23或更高版本碰到壓縮指針造成的問題的話,顯式設(shè)置 -XX:-UseCompressedOops 。 我能做的建議是如果在64位Oracle/Sun JDK 6/7上,那個參數(shù)不要顯式設(shè)置。 關(guān)于HotSpot VM的ergonomics自動開啟壓縮指針功能,請參考之前的一帖。 有些庫比較“聰明”,會自行讀取VM參數(shù)來調(diào)整自己的一些參數(shù),例如Berkeley DB Java Edition。但這些庫實現(xiàn)得不好的時候反而會帶來一些麻煩:BDB JE要求顯式指定-XX:+UseCompressedOops才能有效的調(diào)整它的緩存大小。所以在用BDB JE并且Java堆+PermGen大小小于32GB的時候,請顯式指定-XX:+UseCompressedOops吧。參考Warning on Compressed Oops?
======================================================================?
20、-XX:LargePageSizeInBytes=128m ? 或者是 -XX:LargePageSizeInBytes=256m ? 其實這個參數(shù)的值是多少不是問題,問題是這個參數(shù)到底有沒有起作用。 或許有人讀過很老的調(diào)優(yōu)建議資料,例如這個: (2005) Java Tuning White Paper - 4.2.3 Tuning Example 3: Try 256 MB pages 或者是別的一些內(nèi)容很老的資料。它們提到了-XX:LargePageSizeInBytes=參數(shù)。這些老資料也沒說錯,在Sun JDK 5里 -XX:LargePageSizeInBytes= 參數(shù)只在Solaris上有效,使用的時候沒有別的參數(shù)保護。 但是,實際上這個參數(shù)在Oracle/Sun JDK 6里不配合-XX:+UseLargePages的話是不會起任何作用的。 JDK 6里的JVM的Linux版上初始化large page的地方: C++代碼 收藏代碼bool os::large_page_init() { if (!UseLargePages) return false; if (LargePageSizeInBytes) { _large_page_size = LargePageSizeInBytes; } else { // ... } // ... // Large page support is available on 2.6 or newer kernel, some vendors // (e.g. Redhat) have backported it to their 2.4 based distributions. // We optimistically assume the support is available. If later it turns out // not true, VM will automatically switch to use regular page size. return true; } 看到了么,沒有將UseLargePages設(shè)置為true的話,LargePageSizeInBytes根本沒機會被用上。 對應(yīng)的,Solaris版: C++代碼 收藏代碼bool os::large_page_init() { if (!UseLargePages) { UseISM = false; UseMPSS = false; return false; } // ... } 以及Windows版: C++代碼 收藏代碼bool os::large_page_init() { if (!UseLargePages) return false; // ... } 在Oracle/Sun JDK 6以及Oracle JDK 7上要使用 -XX:LargePageSizeInBytes= 的話,請務(wù)必也設(shè)置上 -XX:+UseLargePages 。使用這兩個參數(shù)之前最好先確認操作系統(tǒng)是否真的只是large pages;操作系統(tǒng)不支持的話,設(shè)置這兩個參數(shù)也沒作用,只會退回到使用regular pages而已。?
======================================================================
?21、-XX:+AlwaysPreTouch 會把commit的空間跑循環(huán)賦值為0以達到“pretouch”的目的。開這個參數(shù)會增加VM初始化時的開銷,但后面涉及虛擬內(nèi)存的開銷可能降低。 在另一個討論帖里有講該參數(shù):http://hllvm.group.iteye.com/group/topic/28839#209144?
======================================================================
?22、-XX:+UseTLAB 與 Runtime.freeMemory()
?======================================================================?
23、-XX:+ParallelRefProcEnabled 這個功能可以加速reference processing,但在JDK6u25和6u26上不要使用,有bug: Bug ID 7028845: CMS: 6984287 broke parallel reference processing in CMS
?======================================================================?
24、-XX:+UseConcMarkSweepGC 與 -XX:+UseAdaptiveSizePolicy 這兩個選項在現(xiàn)有的Oracle/Sun JDK 6和Oracle JDK 7上都不要搭配在一起使用——CMS用的adaptive size policy還沒實現(xiàn)完,用的話可能會crash。 目前HotSpot VM上只有ParallelScavenge系的GC才可以配合-XX:+UseAdaptiveSizePolicy使用;也就是只有-XX:+UseParallelGC或者-XX:+UseParallelOldGC。Jon Masamitsu在郵件列表上提到過。 題外話:開著UseAdaptiveSizePolicy的ParallelScavenge會動態(tài)調(diào)整各空間的大小,有可能會造成兩個survivor space的大小被調(diào)整得不一樣大。Jon Masamitsu在這封郵件里解釋了原因。 ======================================================================?
25、-XX:+UseAdaptiveGCBoundary JDK 6里不要用這個選項,有bug。?
======================================================================
?26、-XX:HeapDumpPath 與 -XX:+HeapDumpBeforeFullGC、-XX:+HeapDumpAfterFullGC、-XX:+HeapDumpOnOutOfMemoryError -XX:+HeapDumpBeforeFullGC、-XX:+HeapDumpAfterFullGC、-XX:+HeapDumpOnOutOfMemoryError 這幾個參數(shù)可以在不同條件下做出HPROF格式的heap dump。但很多人都會疑惑:做出來的heap dump存到哪里去了? 如果不想費神去摸索到底各種環(huán)境被配置成什么樣、“working directory”到底在哪里的話,就在VM啟動參數(shù)里加上 -XX:HeapDumpPath=一個絕對路徑 吧。這樣,自動做出的heap dump就會被存到指定的目錄里去。 當(dāng)然相對路徑也支持,不過用了相對路徑就又得弄清楚當(dāng)前的“working directory”在哪里了。 ======================================================================?
26、UseDepthFirstScavengeOrder 以前有過這樣一個參數(shù)可以設(shè)置young gen遍歷對象圖的順序,深度還是廣度優(yōu)先不過高于JDK 6 update 22就沒用了,ParallelScavenge變?yōu)橹挥蒙疃葍?yōu)先而不用廣度優(yōu)先。 具體的changeset在這里:http://hg.openjdk.java.net/hsx/hotspot-main/hotspot/rev/9d7a8ab3736b HotSpot VM里的arguments.cpp文件里有obsolete_jvm_flags數(shù)組,那邊聲明的參數(shù)都要留意是已經(jīng)沒用的。
總結(jié)
以上是生活随笔為你收集整理的JVM参数调整,经验分享,转载自撒迦的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【转载】 突破技术瓶颈 GIS从二维到三
- 下一篇: 降噪耳机哪个牌子好?国产降噪耳机推荐