“java -server”和“java -client”之间有什么真正的实际区别吗?
我在 Sun 的网站上只能找到一个模糊的
“-server 启动速度较慢,但应该运行得更快”。
真正的区别是什么?(目前使用 JDK 1.6.0_07。)
“java -server”和“java -client”之间有什么真正的实际区别吗?
我在 Sun 的网站上只能找到一个模糊的
“-server 启动速度较慢,但应该运行得更快”。
真正的区别是什么?(目前使用 JDK 1.6.0_07。)
这实际上与HotSpot和客户端和服务器配置之间不同的默认选项值(Java HotSpot VM Options )相关联。
来自白皮书的第 2 章( Java HotSpot 性能引擎架构):
JDK 包括两种类型的 VM——客户端产品和针对服务器应用程序调整的 VM。这两个解决方案共享 Java HotSpot 运行时环境代码库,但使用适合客户端和服务器明显独特的性能特征的不同编译器。这些差异包括编译内联策略和堆默认值。
尽管服务器虚拟机和客户端虚拟机相似,但服务器虚拟机已经过专门调整,以最大限度地提高峰值运行速度。它旨在执行长时间运行的服务器应用程序,这些应用程序需要尽可能快的运行速度,而不是快速启动时间或更小的运行时内存占用。
客户端 VM 编译器用作经典 VM 和早期版本 JDK 使用的即时 (JIT) 编译器的升级。客户端 VM 为应用程序和小程序提供了改进的运行时性能。Java HotSpot 客户端 VM 经过专门调整,可减少应用程序启动时间和内存占用,使其特别适合客户端环境。一般来说,客户端系统更适合 GUI。
所以真正的区别也在于编译器级别:
客户端虚拟机编译器不会尝试执行服务器虚拟机中的编译器执行的许多更复杂的优化,但作为交换,它需要更少的时间来分析和编译一段代码。这意味着客户端 VM 可以更快地启动并且需要更小的内存占用。
服务器 VM 包含一个高级自适应编译器,它支持通过优化 C++ 编译器执行的许多相同类型的优化,以及一些传统编译器无法完成的优化,例如跨虚拟方法调用的激进内联。这是相对于静态编译器的竞争和性能优势。自适应优化技术的方法非常灵活,通常甚至优于高级静态分析和编译技术。
注意:jdk6 update 10的发布(请参阅更新发布说明:1.6.0_10 中的更改)试图改善启动时间,但与热点选项不同的原因是,使用更小的内核以不同的方式打包。
G. Demecki在评论中指出,在 JDK 的 64 位版本中,该-client
选项被忽略了很多年。
请参阅Windowsjava
命令:
-client
选择 Java HotSpot 客户端 VM。
支持 64 位的 JDK 当前忽略此选项,而是使用 Java Hotspot Server VM。
2022:Holger在评论中引用了JavaSE6 / Server-Class Machine Detection,并补充说:
只有在 32 位 Windows 系统上,
-client
才会无条件选择。
其他系统检查机器是否为“服务器类”,当具有至少 2 个内核和至少 2GiB 内存时,该机器是否满足。这就解释了为什么现在几乎所有东西都使用
-server
了相当长的一段时间。即使是你能找到的最便宜的电脑,也是“服务器级”机器。Sun/Oracle 64 版本甚至没有附带客户端 JVM。
旧版本 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 删除了标志并重新删除了服务器和客户端之间的许多区别。
还请记住,您可以jvm
使用jvisualvm
. 如果您的用户或模块设置JAVA_OPTS
或使用更改命令行选项的脚本,这将非常有用。这还可以让您实时监控堆和永久空间使用情况以及许多其他统计信息。
-client 和 -server 系统是不同的二进制文件。它们本质上是两个不同的编译器 (JIT),它们连接到同一个运行时系统。客户端系统最适合需要快速启动时间或占用空间小的应用程序,服务器系统最适合整体性能最重要的应用程序。一般来说,客户端系统更适合交互式应用程序,例如 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
似乎服务器系统的更积极优化,删除循环,因为它知道它不执行任何操作!
我刚刚注意到的一个区别是,在“客户端”模式下,JVM 似乎实际上将一些未使用的内存还给了操作系统,而在“服务器”模式下,一旦 JVM 获取了内存,它就不会给它背部。无论如何,这就是它在带有 Java6 的 Solaris 上的显示方式(prstat -Z
用于查看分配给进程的内存量)。
Oracle 的在线文档提供了一些有关 Java SE 7 的信息。
在java – Windows 的 Java 应用程序启动器页面上,该-client
选项在 64 位 JDK 中被忽略:
选择 Java HotSpot 客户端 VM。支持 64 位的 jdk 当前忽略此选项,而是使用 Java HotSpot Server VM。
但是(为了让事情变得有趣),-server
它指出:
选择 Java HotSpot 服务器虚拟机。在支持 64 位的 jdk 上,仅支持 Java HotSpot Server VM,因此 -server 选项是隐式的。这在未来的版本中可能会发生变化。
Server-Class Machine Detection页面提供有关操作系统和体系结构选择的 VM 的信息。
我不知道这在多大程度上适用于 JDK 6。
来自 Goetz - Java 并发实践:
- 调试提示:对于服务器应用程序,请务必
-server
在调用 JVM 时始终指定 JVM 命令行开关,即使是开发和测试。服务端 JVM 比客户端 JVM 执行更多的优化,比如将循环中没有被修改的变量提升到循环之外;看起来在开发环境(客户端 JVM)中工作的代码可能会在部署环境(服务器 JVM)中中断。例如,如果我们“忘记”在清单 3.4 中将变量 sleep 声明为 volatile,服务器 JVM 可以将测试提升出循环(将其变成无限循环),但客户端 JVM 不会。出现在开发中的无限循环的成本远低于仅出现在生产中的无限循环。
清单 3.4。数羊。
volatile boolean asleep; ... while (!asleep) countSomeSheep();
我的重点。YMMV
IIRC 服务器虚拟机在启动时进行了更多的热点优化,因此它运行得更快,但启动时间更长,并且使用了更多内存。客户端 VM 推迟了大部分优化以允许更快的启动。
编辑添加:这是来自 Sun 的一些信息,它不是很具体,但会给你一些想法。
我没有注意到两者之间的启动时间有任何差异,但是使用“-server”(Solaris 服务器,每个人都使用 SunRays 运行应用程序)对应用程序性能的改进非常小。那是在1.5以下。
上次我看了这个,(诚然,这是不久前的事了)我注意到的最大区别在于垃圾收集。
IIRC:
如果您可以使用jvisualvm工具比较两个 java VM,一个客户端,一个服务器,您应该会看到垃圾收集的频率和效果以及代数方面的差异。
我有两张截图可以很好地显示差异,但我无法重现,因为我有一个 64 位 JVM,它只实现了服务器 VM。(而且我也懒得在我的系统上下载和争论 32 位版本。)
这似乎不再是这种情况了,尝试在具有服务器和客户端 VM 的 Windows 上运行一些代码后,我似乎得到了相同的生成模型......
当从 1.4 迁移到 1.7("1.7.0_55") 版本时。我们在这里观察到的是,在客户端和服务器模式下分配给 heapsize|permsize|ThreadStackSize 参数的默认值没有这种差异。
顺便说一句,(http://www.oracle.com/technetwork/java/ergo5-140223.html)。这是从上面的链接中截取的片段。
initial heap size of 1/64 of physical memory up to 1Gbyte
maximum heap size of ¼ of physical memory up to 1Gbyte
ThreadStackSize 在 1.7 中更高,通过 Open JDK 论坛,有讨论说 1.7 版本的帧大小略高。相信可以根据您的应用程序的行为在运行时测量真正的差异