Реальные различия между "java -server" и "java -client"?
Есть ли реальная практическая разница между "java -server" и "java -client"? Все, что я могу найти на сайте Sun, это расплывчато: "сервер запускается медленнее, но должен работать быстрее". Каковы реальные различия? (В настоящее время используется JDK 1.6.0_07.)
11 ответов
Это действительно связано с HotSpot и значениями параметров по умолчанию ( Java HotSpot VM Options), которые отличаются в зависимости от конфигурации клиента и сервера.
Из главы 2 документа ( Архитектура движка производительности Java HotSpot):
JDK включает в себя два варианта виртуальной машины - предложение на стороне клиента и виртуальную машину, настроенную для серверных приложений. Эти два решения совместно используют базу кода среды выполнения Java HotSpot, но используют разные компиляторы, которые соответствуют совершенно уникальным характеристикам производительности клиентов и серверов. Эти различия включают политику компиляции компиляции и значения по умолчанию для кучи.
Хотя виртуальные машины сервера и клиента похожи, виртуальная машина сервера была специально настроена для максимизации пиковой скорости работы. Он предназначен для выполнения долго работающих серверных приложений, которым требуется максимально высокая скорость работы, превышающая быстрое время запуска или меньший объем памяти во время выполнения.
Компилятор клиентской виртуальной машины служит обновлением как для классической виртуальной машины, так и для JIT-компиляторов, используемых в предыдущих версиях JDK. Клиентская виртуальная машина предлагает улучшенную производительность во время выполнения для приложений и апплетов. Клиентская виртуальная машина Java HotSpot была специально настроена для сокращения времени запуска приложения и использования памяти, что делает его особенно подходящим для клиентских сред. В целом, клиентская система лучше для GUI.
Таким образом, реальная разница также на уровне компилятора:
Компилятор клиентской виртуальной машины не пытается выполнить многие из более сложных оптимизаций, выполняемых компилятором в виртуальной машине сервера, но в обмен на это требуется меньше времени для анализа и компиляции фрагмента кода. Это означает, что клиентская виртуальная машина может запускаться быстрее и требует меньшего объема памяти.
Виртуальная машина сервера содержит усовершенствованный адаптивный компилятор, который поддерживает многие из тех же типов оптимизации, которые выполняются путем оптимизации компиляторов C++, а также некоторые оптимизации, которые не могут быть выполнены традиционными компиляторами, такие как агрессивное встраивание между вызовами виртуальных методов. Это конкурентное и эксплуатационное преимущество перед статическими компиляторами. Технология адаптивной оптимизации очень гибка в своем подходе и обычно превосходит даже передовые методы статического анализа и компиляции.
Примечание. В выпуске обновления 10 для jdk6 (см. Примечания к выпуску для обновления: Изменения в 1.6.0_10) была предпринята попытка улучшить время запуска, но по другой причине, нежели параметры горячей точки, поскольку она была упакована по-другому с гораздо меньшим ядром.
G. Demecki комментариях G. Demecki отмечает, что в 64-битных версиях JDK -client
опция игнорируется на протяжении многих лет.
См Windows java
команда:
-client
Выбирает клиентскую виртуальную машину Java HotSpot.
JDK с поддержкой 64-разрядных систем в настоящее время игнорирует эту опцию и вместо этого использует виртуальную машину Java Hotspot Server.
Наиболее заметным непосредственным отличием в старых версиях 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
или используйте сценарии, которые изменяют параметры командной строки. Это также позволит вам в режиме реального времени отслеживать использование кучи и пространства permgen вместе со многими другими показателями.
Системы -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--) {
}
}
}
Примечание: код был скомпилирован только один раз! Классы одинаковы в обоих заездах!
С -клиентом:
java.exe -client -classpath C: \ mywork \ classes com.blogspot.sdoulger.LoopTest
Потрачено времени: 766
С -сервером:
java.exe -server -classpath C: \ mywork \ classes com.blogspot.sdoulger.LoopTest
Потраченного времени: 0
Похоже, что для более агрессивной оптимизации серверной системы удалите цикл, поскольку он понимает, что не выполняет никаких действий!
Одно отличие, которое я только что заметил, заключается в том, что в режиме "клиент" JVM фактически возвращает оператору неиспользуемую память, тогда как в режиме "сервер", когда JVM захватывает память, он не дает ее. назад. Вот как это выглядит в Solaris с Java6 в любом случае (используя prstat -Z, чтобы увидеть объем памяти, выделенной для процесса).
Онлайн-документация Oracle содержит некоторую информацию для Java SE 7.
На Java - страница запуска приложений Java для Windows, -client
опция игнорируется в 64-битном JDK:
Выберите клиентскую виртуальную машину Java HotSpot. В настоящее время 64-разрядный jdk игнорирует эту опцию и вместо этого использует виртуальную машину Java HotSpot Server.
Однако (чтобы сделать вещи интересными), под -server
Говорится:
Выберите виртуальную машину Java HotSpot Server. На 64-битном jdk поддерживается только виртуальная машина Java HotSpot Server, поэтому опция -server неявна. Это может быть изменено в будущем выпуске.
Страница обнаружения машин серверного класса содержит информацию о том, какая виртуальная машина выбрана ОС и архитектурой.
Я не знаю, насколько это относится к JDK 6.
Из Goetz - Java параллелизма на практике:
- Совет по отладке: для серверных приложений обязательно всегда указывайте ключ командной строки -server JVM при вызове JVM, даже для разработки и тестирования. Серверная JVM выполняет большую оптимизацию, чем клиентская JVM, например, вывод переменных из цикла, которые не были изменены в цикле; код, который может работать в среде разработки (клиентская JVM), может сломаться в среде развертывания (серверная JVM). Например, если бы мы "забыли" объявить переменную "спящий" как энергозависимую в листинге 3.4, серверная JVM могла бы вывести тест из цикла (превратив его в бесконечный цикл), но клиентская JVM этого не сделала бы. Бесконечный цикл, который обнаруживается в разработке, намного дешевле, чем тот, который проявляется только в производстве.
Листинг 3.4. Считать овец.
volatile boolean asleep; ... while (!asleep) countSomeSheep();
Мой акцент. YMMV
IIRC серверная виртуальная машина выполняет больше оптимизаций горячих точек при запуске, поэтому она работает быстрее, но запускается немного дольше и использует больше памяти. Клиентская виртуальная машина откладывает большую часть оптимизации, чтобы обеспечить более быстрый запуск.
Редактировать, чтобы добавить: Вот некоторая информация от Sun, она не очень конкретна, но даст вам некоторые идеи.
IIRC, это включает в себя стратегии сбора мусора. Теория состоит в том, что клиент и сервер будут отличаться с точки зрения недолговечных объектов, что важно для современных алгоритмов GC.
Вот ссылка на режим сервера. Увы, они не упоминают режим клиента.
Вот очень полная ссылка на GC в целом; это более простая статья. Не уверен, что любой адрес -server vs -client, но это релевантный материал.
В "No Fluff Just Stuff" и Кен Сипе, и Гленн Ванденбург делают отличные разговоры о подобных вещах.
Я не заметил какой-либо разницы во времени запуска между двумя, но достиг минимального улучшения производительности приложения с помощью "-server" (сервер Solaris, все используют SunRays для запуска приложения). Это было под 1,5.
В прошлый раз, когда я взглянул на это (и по общему признанию, это было некоторое время назад), самое большое различие, которое я заметил, было в сборке мусора.
IIRC:
- Виртуальная куча сервера имеет различное число поколений, чем виртуальная машина клиента, и другой алгоритм сбора мусора. Это может быть не так больше
- Виртуальная машина сервера выделит память и не освободит ее для ОС
- Виртуальная машина сервера будет использовать более сложные алгоритмы оптимизации и, следовательно, будет иметь больше времени и памяти для оптимизации.
Если вы можете сравнить две виртуальные машины Java, один клиент и один сервер с помощью инструмента jvisualvm, вы увидите разницу в частоте и эффективности сборки мусора, а также в количестве поколений.
У меня была пара скриншотов, которые очень хорошо показали разницу, но я не могу воспроизвести, поскольку у меня есть 64-битная JVM, которая реализует только виртуальную машину сервера. (И я не могу быть обеспокоен, чтобы загрузить и изменить 32-битную версию в моей системе.)
Похоже, что это больше не так, попробовав запустить некоторый код в 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. Считается, что реальную разницу можно было бы измерить во время выполнения на основе вашего поведения приложения