"java -server"和"java -client"之间的实际区别是什么?

423

在使用"java -server" 和 "java -client"命令时,是否存在实际可感知的差异?

我在Sun的网站上只找到了含糊的描述:

"-server 启动较慢,但应该更快地运行"。

它们之间的真正区别是什么?(当前使用JDK 1.6.0_07版本。)


1
简短回答:如果你今天正在阅读这篇文章,使用云端中的64位>=Java 8 ……呃,不需要。 - conny
11个回答

397
这实际上与Java HotSpot VM选项有关,并且默认的选项值与客户端和服务器配置不同。
从白皮书的第二章(Java HotSpot Performance Engine Architecture)中可知:
JDK包括两种VM版本,一种是面向客户端的版本,另一种是针对服务器应用程序调整过的VM版本。这两种解决方案共享Java HotSpot运行时环境代码库,但使用不同的编译器,适合于客户端和服务器的性能特征。这些区别包括编译内联策略和堆默认值。 虽然Server VM和Client VM相似,但Server VM已经特别调整以最大化峰值操作速度。它旨在执行长时间运行的服务器应用程序,这些应用程序需要最快的操作速度而不是快速启动时间或更小的运行时内存占用。 Client VM编译器是Classic VM和之前JDK版本使用的即时(JIT)编译器的升级。Client VM为应用程序和小应用程序提供了改进的运行时性能。Java HotSpot Client VM已经特别调整,以减少应用程序启动时间和内存占用空间,使其特别适用于客户端环境。总的来说,客户端系统更适合GUI。
因此,真正的差异也在于编译器层面:
客户端VM编译器不会尝试执行服务器VM编译器执行的许多更复杂的优化,但作为交换,它需要更少的时间来分析和编译代码。这意味着客户端VM可以更快地启动,并且需要更小的内存占用。
服务器VM包含一个先进的自适应编译器,支持许多与优化C ++编译器执行相同类型的优化,以及一些传统编译器无法完成的优化,例如跨虚拟方法调用进行积极内联。这是静态编译器的竞争和性能优势。自适应优化技术在其方法上非常灵活,并且通常优于甚至高级静态分析和编译技术。
注意:jdk6 update 10的发布(请参见Update Release Notes:Changes in 1.6.0_10)试图改善启动时间,但出于与热点选项不同的原因,它被以不同的方式打包,并具有更小的内核。

G. Demecki指出在评论中,在64位JDK版本中,-client选项已经被忽略了多年。
请参见Windows java命令

-client

选择Java HotSpot客户端VM。
64位JDK当前忽略此选项,而是使用Java Hotspot服务器VM。

2022年:Holger评论中提到了JavaSE6 / 服务器级机器检测,并补充道:

仅在32位Windows系统上,-client被无条件选择。
其他系统会检查机器是否为“服务器级”,当至少有2个内核和至少2GiB的内存时才能满足。

这就解释了为什么几乎所有东西现在都使用-server。即使是你能找到的最便宜的电脑,也是“服务器级”机器。Sun/Oracle的64位版本甚至没有提供客户端JVM。


8
从JDK6 update 10版本开始,有一个后台进程将运行时库保留在内存中,使得新进程启动比按需分页要快得多。 - Thorbjørn Ravn Andersen
1
虽然客户端虚拟机也进行了积极的内联,但没关系。 - Thorbjørn Ravn Andersen
2
我认为这个答案应该被更新。因为在JDK的64位版本中,-client选项已经被忽略了很多年。 - G. Demecki
1
@G.Demecki 确定:您有文档链接证明该选项已过时或被忽略吗? - VonC
2
当然。这里是Java 7在Windows上的一些文档。令人惊讶的是,类似的信息也可以在Java 6的文档中找到。 - G. Demecki
显示剩余9条评论

94

在旧版Java中最明显的即时区别是分配给-client-server应用的内存不同。例如,在我的Linux系统上,我得到如下结果:

$ 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"

默认情况下,它使用-server参数,但使用-client选项后我得到:

$ 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 标志的话,这个 Java 版本的大多数内存限制和初始分配都会更高。

然而,不同的体系结构、操作系统和 jvm 版本的组合可能会导致这些值发生变化。最近的 jvm 版本已经删除了一些标志,并消除了许多 server 和 client 之间的差异。

还要记住,可以通过使用 jvisualvm 来查看运行中的 jvm 的所有详细信息。如果您有设置 JAVA_OPTS 或使用更改命令行选项的脚本的用户或模块,则这将很有用。这也可以让您实时监视堆和 permgen 空间的使用以及许多其他统计信息。


2
在CentOS 7上,对于Java版本“1.7.0_79”,无论是在-server模式还是-client模式下,我得到的数字都是相同的。 [Java(TM) SE Runtime Environment (build 1.7.0_79-b15) Java HotSpot(TM) 64-Bit Server VM] - Basil Musa
4
这就是我提供答案的原因。重点不在于价值观,而在于让任何人在任何时候都能够找到适用于其特定jvm版本的答案。 - Mark Booth

40

-client和-server系统是不同的二进制文件。它们本质上是两个不同的编译器(JITs),接口到同一个运行时系统。对于需要快速启动时间或小的占用空间的应用程序,客户端系统是最佳选择;而服务器系统则适用于整体性能最重要的应用程序。通常情况下,客户端系统更适合交互式应用程序,例如GUI。

输入图像描述

我们使用两个开关同时运行以下代码:

package com.blogspot.sdoulger;

public class LoopTest {
    public LoopTest() {
        super();
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        spendTime();
        long end = System.currentTimeMillis();
        System.out.println("Time spent: "+ (end-start));

        LoopTest loopTest = new LoopTest();
    }

    private static void spendTime() {
        for (int i =500000000;i>0;i--) {
        }
    }
}

注意: 代码只编译一次!两次运行的类是相同的!

使用 -client:
java.exe -client -classpath C:\mywork\classes com.blogspot.sdoulger.LoopTest
花费时间: 766

使用 -server:
java.exe -server -classpath C:\mywork\classes com.blogspot.sdoulger.LoopTest
花费时间: 0

似乎服务器系统更积极的优化,将循环删除,因为它理解该循环不执行任何操作!

参考资料


34

我刚刚注意到的一个区别是,在“客户端”模式下,JVM似乎会将一些未使用的内存返还给操作系统,而在“服务器”模式下,一旦JVM获取了内存,就不会将其归还。至少在Solaris上使用Java6时是这样的(使用prstat -Z查看进程分配的内存量)。


24

Oracle的在线文档提供了一些关于Java SE 7的信息。

在Windows系统上的java - Java应用程序启动器页面中,64位JDK将忽略-client选项:

选择Java HotSpot Client VM。64位JDK当前会忽略此选项并使用Java HotSpot Server VM。

然而(为了使事情变得有趣),在-server选项下,它表示:

选择Java HotSpot Server VM。对于支持64位的JDK只支持Java HotSpot Server VM因此-server选项是隐式的。这在未来版本中可能会更改。

服务器级别机器检测页面提供了有关操作系统和架构所选虚拟机的信息。

我不知道其中多少适用于JDK 6。


2
谢谢,我在想为什么在JDK7上没有看到client/jvm.dll。 - Archimedes Trajano

18

来自Goetz - Java并发实践:

  1. 调试技巧:对于服务器应用程序,在调用JVM时一定要始终指定-server JVM命令行开关,即使是在开发和测试阶段也要这样做。服务器JVM执行了比客户端JVM更多的优化,例如将未在循环中修改的变量提升出循环; 在开发环境(客户端JVM)工作正常的代码在部署环境(服务器JVM)可能会出问题。例如,在第3.4节的示例中,如果我们“忘记”在asleep变量中声明volatile,则服务器JVM可以将测试提升出循环(将其转换为无限循环),但客户端JVM不会。在开发中出现的无限循环比仅在生产中出现的无限循环成本要低得多。

清单3.4。数羊。

volatile boolean asleep;
...
while (!asleep)
   countSomeSheep();

我的强调。你的经验可能有所不同。


15

据我所知,服务器虚拟机在启动时进行更多的热点优化,因此运行速度更快,但启动时间稍长且使用的内存更多。客户端虚拟机将大部分优化推迟以实现更快的启动。

编辑添加:这里是来自Sun的一些信息,它并不是非常具体,但会给您一些想法。


5
我IRC,它涉及垃圾回收策略。理论上,客户端和服务器在短暂存在的对象方面是不同的,这对于现代GC算法非常重要。 这里是关于服务器模式的链接。遗憾的是,他们没有提到客户端模式。 这是一个关于GC的非常详细的链接;这是一篇更基础的文章。不确定其中任何一篇文章是否涉及-server与-client,但这是相关材料。
在No Fluff Just Stuff上,Ken Sipe和Glenn Vandenburg都做了很棒的演讲。

3

我没有注意到启动时间上的任何差异,但是在应用程序性能方面使用"-server"(Solaris服务器,每个人都使用SunRays运行应用程序)有一些微小的改进。这是在1.5版本下测试的。


6
取决于你的程序在做什么。对于一些重复执行相同操作的处理器密集型应用程序,我注意到使用-server可以实现巨大的性能提升(高达10倍)。 - Dan Dyer
1
丹,你有这方面的参考资料吗?我想进一步调查。 - Thorbjørn Ravn Andersen
1
使用服务器虚拟机运行Sunflow比客户端快得多。http://sunflow.sourceforge.net/ - John

2
上次我看到这个时候(承认是一段时间以前),我注意到最大的区别在于垃圾回收。
如果我没记错: - 服务器堆 VM 比客户端 VM 有不同数量的世代,以及不同的垃圾回收算法。(现在可能不再是这样) - 服务器 VM 将分配内存并不释放给操作系统。 - 服务器 VM 将使用更复杂的优化算法,因此对于优化而言需要更多的时间和内存。 如果您使用 jvisualvm 工具比较两个 Java VM,一个是客户端,一个是服务器,则应该会看到垃圾回收的频率和效果有所不同,世代的数量也不同。 我曾经有一对截图,非常清楚地显示了这种差异,但我现在无法再现了,因为我的 64 位 JVM 只实现了服务器 VM。(而且我懒得在我的系统上下载并处理 32 位版本。)
尝试在 Windows 上使用服务器和客户端 VM 运行一些代码后,似乎不再是这种情况,我似乎得到了相同的世代模型...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接