原文出处: 摆渡者

引言

曾几何时,我也敲打过无数次这样的命令:

然而之前的我都只关心过版本号,也就是第一行的内容。今天,我们就来看看第3行输出的内容:JVM的类型和工作模式。

其实说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文档中读所有的类数据的情况下显示出性能优势。

JVM的类型

通过百度搜索,只能搜到几篇被重复转载的文章。比如这一篇,这里面基本描述了两种类型的JVM的区别:

-Server VM启动时,速度较慢,但是一旦运行起来后,性能将会有很大的提升。

但我认为仅仅知道这些区别还不够。然而,我在百度的搜索结果中很少看见有描述的比较深入的关于JVM类型和模式区别的文章。不过我倒是找到了这一篇文章。 这篇文章中提到了如下内容:

当虚拟机运行在-client模式的时候,使用的是一个代号为C1的轻量级编译器, 而-server模式启动的虚拟机采用相对重量级,代号为C2的编译器. C2比C1编译器编译的相对彻底,,服务起来之后,性能更高.

对于这个结果,我觉得还是不够深入。于是FQ通过Google搜索,前几条即为我想要的结果。

两种类型JVM的区别

那么,Client JVM和Server JVM到底在哪些方面不同呢?Oracle官方网站的高频问题上这么解释的:

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类型的切换

上面的运行结果中还提到了如何切换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的原因了。现在将第33、34行配置交换一下,再在命令行中输入java -version,则会得到以下结果:

这就将JVM的工作模式切换到Server了,这个修改是全局的,以后使用到的这个JVM都是工作在Server模式的。

当然,如果你不想全局改动,也可以按照下面在java命令后加上-server或者-client来明确指定本次java命令需要JVM使用何种模式来工作,例如:

这个就是语句级的修改了。

注意,不管是全局修改还是语句级的修改,实际上会导致下次执行Java程序时会使用对应目录下的jvm.dll。如何证明?这里我将%JAVA_HOME%\jre\bin下面的server文件夹移动到其他位置,再次运行java -server -version命令,则会出现下面的错误:

JVM的工作模式

JVM的几种工作模式

在命令行里输入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的工作模式

在JVM运行时,我们可以通过下列代码检查JVM的类型和工作模式:

1
2
System.out.println(System.getProperty("java.vm.name")); //获取JVM名字和类型
System.out.println(System.getProperty("java.vm.info")); //获取JVM的工作模式

你可能得到以下结果:

参考页面:

http://www.oracle.com/technetwork/java/hotspotfaq-138619.html#compiler_types

http://docs.oracle.com/javase/6/docs/technotes/guides/vm/server-class.html

http://stackoverflow.com/questions/198577/real-differences-between-java-server-and-java-client

http://www.javacodegeeks.com/2011/07/jvm-options-client-vs-server.html

https://blog.codecentric.de/en/2012/07/useful-jvm-flags-part-1-jvm-types-and-compiler-modes/

关于JVM的类型和模式的更多相关文章

  1. PHP严格类型检查模式

    前言 PHP默认情况下是弱类型校验模式,在php7下declare新增了strict_types指令,通过设置strict_types的值(1或者0),1表示严格类型校验模式,作用于函数调用和返回语句 ...

  2. jvm运行时内存模式

    jvm内存模型 内存模型粗略划分为:堆和栈 详细划分为:堆,虚拟机栈,方法区,本地方法区,程序计数器 程序计数器: 为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程 ...

  3. JVM参数类型

    java -version看版本号(混合模式) java -Xint -version  解释执行 java -Xcomp -version 编译执行 XX参数是不稳定的用来JVM调优和DeBug B ...

  4. JVM 参数类型

    标准参数 -help -server -client -version -showversion -cp -classpath X参数 非标准化参数(在各个JDK版本中可能会变,但是变动比较小) -X ...

  5. JVM参数(一)JVM类型以及编译器模式

    现在的JVM运行Java程序(和其它的兼容性语言)时在高效性和稳定性方面做的非常出色.自适应内存管理.垃圾收集.及时编译.动态类加载.锁优化——这里仅仅列举了某些场景下会发生的神奇的事情,但他们几乎不 ...

  6. JVM实用参数(一)JVM类型以及编译器模式

    JVM实用参数(一)JVM类型以及编译器模式 原文地址:https://blog.codecentric.de/en/2012/07/useful-jvm-flags-part-1-jvm-types ...

  7. 【JVM】垃圾回收器总结(2)——七种垃圾回收器类型

    七种垃圾回收器类型 GC的约定参数 DefNew——Default New Generation Tenured——Serial Old ParNew——Parallel New Generation ...

  8. Java魔法堂:JVM的运行模式

    一.前言 JVM有Client和Server两种运行模式.不同的模式对应不同的应用场景,而JVM也会有相应的优化.本文将记录JVM模式的信息,以便日后查阅. 二.介绍 在$JAVA_HOME/jre/ ...

  9. Java魔法堂:JVM的运行模式 (转)

    一.前言 JVM有Client和Server两种运行模式.不同的模式对应不同的应用场景,而JVM也会有相应的优化.本文将记录JVM模式的信息,以便日后查阅. 二.介绍 在$JAVA_HOME/jre/ ...

随机推荐

  1. window server开发

    代码部分: public partial class tv : ServiceBase { public tv() { InitializeComponent(); ServiceName = &qu ...

  2. Python学习遇到的问题

    UnicodeDecodeError: 'gbk' codec can't decode byte 0xae in position

  3. 【T-SQL系列】FOR XML PATH 语句的应用

    DECLARE @TempTable TABLE ( UserID INT , UserName ) ); INSERT INTO @TempTable ( UserID, UserName ) , ...

  4. centos下安装nginx和php-fpm

    安装这两个花了大约七个小时,简直呵呵,安装nginx就是直接 yum install nginx ,但发现一打开php文件就是直接下载该php文件,也就是不能识别php文件,解决这个花了好久,但其实看 ...

  5. mysql概要(九)字符集和校对集

    1.mysql 字符集有细致设置: 2.mysql字符处理机制是:数据库和客户端之间存在一个字符集转换器(后文简称转换器)将客户端字符编码(必须告诉服务端的)转换成一种中间编码的数据(可自定义的但保证 ...

  6. Spring读书笔记-----Spring的Bean之Bean的基本概念

    从前面我们知道Spring其实就是一个大型的工厂,而Spring容器中的Bean就是该工厂的产品.对于Spring容器能够生产那些产品,则取决于配置文件中配置. 对于我们而言,我们使用Spring框架 ...

  7. Java数组实现五子棋功能

    package ch4; import java.io.*; /** * Created by Jiqing on 2016/11/9. */ public class Gobang { // 定义棋 ...

  8. JavaSE复习_11 IO流复习

    △FileReader是使用默认码表读取文件, 如果需要使用指定码表读取, 那么可以使用InputStreamReader(字节流,编码表)    FileWriter是使用默认码表写出文件, 如果需 ...

  9. mysql 命令行快速导出数据,导入数据

    如果数据有20几万以上的时候,下面的方法很实用 导出数据 1.into outfile select * from table into outfile 'C:/a.sql'; 2.mysqldump ...

  10. 转:strcmp函数实现及分析

    转自:strcmp函数实现及详解 strcmp函数是C/C++中基本的函数,它对两个字符串进行比较,然后返回比较结果,函数形式如下:int strcmp(constchar*str1,constcha ...