曾幾什麼時候,我也敲打過無數次這樣的命令: html
然而以前的我都只關心過版本號,也就是第一行的內容。今天,咱們就來看看第3行輸出的內容:JVM的類型和工做模式。 java
其實說Server和Client是JVM的兩種工做模式是不許確的,由於它們就是不一樣的虛擬機,所以應該說有兩種類型的JVM。 算法
第三行的輸出中能夠看到:JVM的名字(HotSpot)、類型(Client)和build ID(24.79-b02) 。除此以外,咱們還知道JVM以混合模式(mixed mode)在運行,這是HotSpot默認的運行模式,意味着JVM在運行時能夠動態的把字節碼編譯爲本地代碼。咱們也能夠看到類數據共享(class data sharing)是開啓(即第三行最後的sharing)的。類數據共享(class data sharing)是一種在只讀緩存(在jsa文件中,」Java Shared Archive」)中存儲JRE的系統類,被全部Java進程的類加載器用來當作共享資源,它可能在常常從jar文檔中讀全部的類數據的狀況下顯示出性能優點。 windows
經過百度搜索,只能搜到幾篇被重複轉載的文章。好比這一篇,這裏面基本描述了兩種類型的JVM的區別: 緩存
-Server VM啓動時,速度較慢,可是一旦運行起來後,性能將會有很大的提高。 oracle
但我認爲僅僅知道這些區別還不夠。然而,我在百度的搜索結果中不多看見有描述的比較深刻的關於JVM類型和模式區別的文章。不過我卻是找到了這一篇文章。 這篇文章中提到了以下內容: app
當虛擬機運行在-client模式的時候,使用的是一個代號爲C1的輕量級編譯器, 而-server模式啓動的虛擬機採用相對重量級,代號爲C2的編譯器. C2比C1編譯器編譯的相對完全,,服務起來以後,性能更高. less
對於這個結果,我以爲仍是不夠深刻。因而FQ經過Google搜索,前幾條即爲我想要的結果。 jvm
那麼,Client JVM和Server JVM到底在哪些方面不一樣呢?Oracle官方網站的高頻問題上這麼解釋的:
ide
These two systems are different binaries. They are essentially two different compilers (JITs)interfacing to the same runtime system. The client system is optimal for applications which need fast startup times or small footprints, the server system is optimal for applications where the overall performance is most important. In general the client system is better suited for interactive applications such as GUIs. Some of the other differences include the compilation policy,heap defaults, and inlining policy.
大意是說,這兩個JVM是使用的不一樣編譯器。Client JVM適合須要快速啓動和較小內存空間的應用,它適合交互性的應用,好比GUI;而Server JVM則是看重執行效率的應用的最佳選擇。不一樣之處包括:編譯策略、默認堆大小、內嵌策略。
根據《The Java HotSpot Performance Engine Architecture》:
The Client VM compiler does not try to execute many of the more complex optimizations performed by the compiler in the Server VM, but in exchange, it requires less time to analyze and compile a piece of code. This means the Client VM can start up faster and requires a smaller memory footprint.
Note: It seems that the main cause of the difference in performance is the amount of optimizations.
The Server VM contains an advanced adaptive compiler that supports many of the same types of optimizations performed by optimizing C++ compilers, as well as some optimizations that cannot be done by traditional compilers, such as aggressive inlining across virtual method invocations. This is a competitive and performance advantage over static compilers. Adaptive optimization technology is very flexible in its approach, and typically outperforms even advanced static analysis and compilation techniques.
Both solutions deliver extremely reliable, secure, and maintainable environments to meet the demands of today’s enterprise customers.
很明顯,Client VM的編譯器沒有像Server VM同樣執行許多複雜的優化算法,所以,它在分析和編譯代碼片斷的時候更快。而Server VM則包含了一個高級的編譯器,該編譯器支持許多和在C++編譯器上執行的同樣的優化,同時還包括許多傳統的編譯器沒法實現的優化。
官方文檔是從編譯策略和內嵌策略分析了兩者的不一樣,下面的命令則從實際的狀況體現了兩者在默認堆大小上的差異:
對於Server JVM:
1
2
3
4
5
6
7
8
9
|
$ java -XX:+PrintFlagsFinal -version 2>&1 | grep -i -E 'heapsize|permsize|version'
uintx AdaptivePermSizeWeight = 20 {product}
uintx ErgoHeapSizeLimit = 0 {product}
uintx InitialHeapSize := 66328448 {product}
uintx LargePageHeapSizeThreshold = 134217728 {product}
uintx MaxHeapSize := 1063256064 {product}
uintx MaxPermSize = 67108864 {pd product}
uintx PermSize = 16777216 {pd product}
java version "1.6.0_24"
|
對於Client JVM:
1
2
3
4
5
6
7
8
9
|
$ java -client -XX:+PrintFlagsFinal -version 2>&1 | grep -i -E 'heapsize|permsize|version'
uintx AdaptivePermSizeWeight = 20 {product}
uintx ErgoHeapSizeLimit = 0 {product}
uintx InitialHeapSize := 16777216 {product}
uintx LargePageHeapSizeThreshold = 134217728 {product}
uintx MaxHeapSize := 268435456 {product}
uintx MaxPermSize = 67108864 {pd product}
uintx PermSize = 12582912 {pd product}
java version "1.6.0_24"
|
能夠很清楚的看到,Server JVM的InitialHeapSize和MaxHeapSize明顯比Client JVM大出許多來。
下面是一個例子,它展現了兩者執行的效率,該例子來自Onkar Joshi’s blog:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class LoopTest {
public static void main(String[] args) {
long start = System.currentTimeMillis();
spendTime();
long end = System.currentTimeMillis();
System.out.println(end-start);
}
private static void spendTime() {
for (int i =500000000;i>0;i--) {
}
}
}
|
注意:這段代碼只編譯一次,只是運行這段代碼的JVM不一樣而已。不要使用Eclipse中的Run As,由於它會將代碼從新編譯。這裏,咱們使用java命令來執行這段代碼:
看到區別了吧?
自動檢測Server-Class機器
如下內容引用自:http://docs.oracle.com/javase/6/docs/technotes/guides/vm/server-class.html
從J2SE 5.0開始,當一個應用啓動的時候,加載器會嘗試去檢測應用是否運行在 "server-class" 的機器上,若是是,則使用Java HotSpot Server Virtual Machine (server VM)而不是 Java HotSpot Client Virtual Machine (client VM)。這樣作的目的是提升執行效率,即便沒有爲應用顯式配置VM。
注意: 從Java SE 6開始, server-class機器的定義是至少有2個CPU和至少2GB的物理內存
下面這張圖展現了各個平臺的默認的JVM(注意:—表明不提供該平臺的JVM ):
-------------------------------引用內容結束--------------------------------------
上面的運行結果中還提到了如何切換JVM的類型,咱們就來看看爲何第一個截圖裏面輸出的是:
1
|
Java HotSpot(TM) Client VM
|
這裏須要注意的是,Oracle網站這樣說:
Client and server systems are both downloaded with the 32-bit Solaris and Linux downloads. For 32-bit Windows, if you download the JRE, you get only the client, you'll need to download the SDK to get both systems.
所以,若是想要在windows平臺下從Client JVM切換到Server JVM,須要下載JDK而非JRE。打開JDK安裝目錄(即%JAVA_HOME%):咱們能夠看到%JAVA_HOME%\jre\bin下有一個server和client文件夾,這裏面各有一個jvm.dll,可是大小不一樣,這就說明了它們是不一樣的JVM的文件:
打開 %JAVA_HOME%\jre\lib\i386\jvm.cfg文件(正如第一幅圖所見,我這裏安裝的是32JDK,其餘版本的JDK可能不是i386文件夾)(注意是JDK文件夾下的jre,而非和JDK同級的jre6/7/8),會注意到如下內容(灰色選中部分):
再看看下方的配置,第一行就配置了使用client方式,所以首選使用client模式的JVM,這就是爲何一開始的java -version命令會輸出Java HotSpot(TM) Client VM的緣由了。如今將第3三、34行配置交換一下,再在命令行中輸入java -version,則會獲得如下結果:
這就將JVM的工做模式切換到Server了,這個修改是全局的,之後使用到的這個JVM都是工做在Server模式的。
固然,若是你不想全局改動,也能夠按照下面在java命令後加上-server或者-client來明確指定本次java命令須要JVM使用何種模式來工做,例如:
這個就是語句級的修改了。
注意,不論是全局修改仍是語句級的修改,實際上會致使下次執行Java程序時會使用對應目錄下的jvm.dll。如何證實?這裏我將%JAVA_HOME%\jre\bin下面的server文件夾移動到其餘位置,再次運行java -server -version命令,則會出現下面的錯誤:
在命令行裏輸入java -X,你會看到如下結果:
其實這兩個是JVM工做的模式。JVM有如下幾種模式:-Xint, -Xcomp, 和 -Xmixed。從上圖的輸出結果中也能夠看到,mixed是JVM的默認模式,其實在文章一開始的時候就提到了,由於在java -version命令中,輸出瞭如下內容:
1
|
Java HotSpot(TM) Client VM (build 24.79-b02, mixed mode, sharing)
|
中間的mixed mode就說明當前JVM是工做在mixed模式下的。-Xint和-Xcomp參數和咱們的平常工做不是很相關,可是我很是有興趣經過它來了解下JVM。
-Xint表明解釋模式(interpreted mode),-Xint標記會強制JVM以解釋方式執行全部的字節碼,固然這會下降運行速度,一般低10倍或更多。如今經過剛纔的例子(沒有從新編譯過)來驗證一下:
能夠看到,在都使用Client JVM的前提下,混合模式下,平均耗時150ms,然而在解釋模式下,平均耗時超過1600ms,這基本上是10倍以上的差距。
-Xcomp表明編譯模式(compiled mode),與它(-Xint)正好相反,JVM在第一次使用時會把全部的字節碼編譯成本地代碼,從而帶來最大程度的優化。這聽起來不錯,由於這徹底繞開了緩慢的解釋器。然而,不少應用在使用-Xcomp也會有一些性能損失,可是這比使用-Xint損失的少,緣由是-Xcomp沒有讓JVM啓用JIT編譯器的所有功能。所以在上圖中,咱們並無看到-Xcomp比-Xmixed快多少。
-Xmixed表明混合模式(mixed mode),前面也提到了,混合模式是JVM的默認工做模式。它會同時使用編譯模式和解釋模式。對於字節碼中屢次被調用的部分,JVM會將其編譯成本地代碼以提升執行效率;而被調用不多(甚至只有一次)的方法在解釋模式下會繼續執行,從而減小編譯和優化成本。JIT編譯器在運行時建立方法使用文件,而後一步一步的優化每個方法,有時候會主動的優化應用的行爲。這些優化技術,好比積極的分支預測(optimistic branch prediction),若是不先分析應用就不能有效的使用。這樣將頻繁調用的部分提取出來,編譯成本地代碼,也就是在應用中構建某種熱點(即HotSpot,這也是HotSpot JVM名字的由來)。使用混合模式能夠得到最好的執行效率。
和切換JVM的類型同樣,咱們能夠在命令行裏顯示指定使用JVM的何種模式,好比:
在JVM運行時,咱們能夠經過下列代碼檢查JVM的類型和工做模式:
1
2
|
System.out.println(System.getProperty("java.vm.name")); //獲取JVM名字和類型
System.out.println(System.getProperty("java.vm.info")); //獲取JVM的工做模式
|
你可能獲得如下結果: