Подтвердить что ты не робот

Почему Java быстрее при использовании JIT или компиляции в машинный код?

Я слышал, что Java должен использовать JIT, чтобы быть быстрым. Это имеет смысл при сравнении с интерпретацией, но почему кто-то не может сделать компилятор с опережающим временем, который генерирует быстрый Java-код? Я знаю о gcj, но я не думаю, что его вывод обычно быстрее, чем Hotspot, например.

Есть ли вещи, которые затрудняют этот язык? Я думаю, что это сводится только к этим вещам:

  • Отражение
  • загрузки классов

Что мне не хватает? Если я избегу этих функций, можно ли скомпилировать код Java один раз на собственный машинный код и сделать?

4b9b3361

Ответ 1

Настоящий убийца для любого компилятора AOT:

Class.forName(...)

Это означает, что вы не можете написать компилятор AOT, который охватывает ВСЕ программы Java, поскольку есть информация, доступная только во время выполнения о характеристиках программы. Вы можете, однако, сделать это на подмножестве Java, что я считаю, что gcj делает.

Другим типичным примером является возможность JIT для встроенных методов, таких как getX(), непосредственно в вызывающих методах, если обнаружено, что это безопасно, и, если необходимо, отменить его, даже если это явно не помогло программист сообщая, что метод является окончательным. JIT может видеть, что в запущенной программе данный метод не переоценивается и поэтому в этом случае может рассматриваться как окончательный. В следующем вызове это может быть другим.

Ответ 2

Компилятор JIT может быть быстрее, потому что машинный код генерируется на точной машине, в которой он также будет выполняться. Это означает, что JIT обладает наилучшей возможной информацией для создания оптимизированного кода.

Если вы предварительно скомпилируете байт-код в машинный код, компилятор не может оптимизировать для целевых машин, только для сборки.

Ответ 3

Я вложу интересный ответ, полученный Джеймс Гослинг в книге Умение программирования.

Ну, я слышал, что он сказал, что эффективно у вас есть два компилятора в мир Java. У вас есть компилятор к байт-коду Java, а затем у вас есть ваш JIT, который в основном перекомпилирует все снова. Все ваши страшные оптимизации находятся в JIT.

Джеймс: Точно. В эти дни избивая действительно хорошие C и С++ компиляторы почти всегда. Когда ты перейдите в динамический компилятор, вы получите два преимущества, когда компиляторы бегущая прямо в последний момент. Один вы точно знаете, какой набор микросхем вы бежите дальше. Так много раз, когда люди компилируют кусок C кода, они должны скомпилировать его для запуска по виду общего x86 архитектура. Почти ни одна из бинарные файлы, которые вы получаете, особенно хороши настроенный для любого из них. Вы загружаете последняя копия Mozilla, и itll работает практически во всех Intel архитектура CPU. В значительной степени один Linux файл. Его довольно общий, и его скомпилированный с помощью GCC, который не очень хороший компилятор C.

Когда HotSpot работает, он точно знает какой чипсет вы работаете. Это точно знает, как работает кеш. Это точно знает, как иерархия памяти работает. Он точно знает, как все межблочные блокировки работают в ЦП. Он знает, какой набор инструкций расширения этого чипа. Это оптимизирует для какой именно машины Вы на. Тогда другая половина заключается в том, что он фактически видит приложения в качестве его запуска. Его способность иметь статистику, которая все важно. Его способность встроенные вещи, которые компилятор C мог никогда не делай. То, что получает встроенный в мир Java довольно удивительно. Затем вы как управление хранилищем работает с современные сборщики мусора. С современный сборщик мусора, хранилище распределение происходит очень быстро.

Masterminds of Programming

Ответ 4

Компилятор Java JIT также ленив и адаптивен.

Ленивый

Будучи ленивым, он только компилирует методы, когда получает их вместо компиляции всей программы (очень полезно, если вы не используете часть программы). Загрузка класса на самом деле помогает ускорить работу JIT, позволяя ей игнорировать классы, с которыми она еще не сталкивалась.

Адаптивный

Будучи адаптивной, он сначала генерирует быструю и грязную версию машинного кода, а затем возвращается только и выполняет сквозное задание, если этот метод используется часто.

Ответ 5

В конце концов, это сводится к тому, что наличие большей информации позволяет улучшить оптимизацию. В этом случае JIT имеет больше информации о фактической машине, на которой работает код (как упоминал Эндрю), а также имеет много информации о времени выполнения, которая недоступна во время компиляции.

Ответ 6

Возможность Java встроить границы виртуальных методов и выполнять эффективную диспетчеризацию интерфейса требует анализа времени выполнения перед компиляцией - другими словами, для этого требуется JIT. Поскольку все методы являются виртуальными, а интерфейсы используются "везде", это имеет большое значение.

Ответ 7

Теоретически, JIT-компилятор имеет преимущество перед AOT , если у него достаточно времени и доступных вычислительных ресурсов. Например, если у вас есть корпоративное приложение, работающее в течение нескольких дней и месяцев на многопроцессорном сервере с большим количеством ОЗУ, компилятор JIT может создавать лучший код, чем любой компилятор AOT.

Теперь, если у вас есть настольное приложение, такие вещи, как быстрый запуск и начальное время отклика (когда AOT светит) становятся более важными, а также может не хватать ресурсов для наиболее продвинутых оптимизаций.

И если у вас встроенная система с ограниченными ресурсами, у JIT нет шансов против AOT.

Однако вышеизложенная была всей теорией. На практике создание такого продвинутого JIT-компилятора намного сложнее, чем приличный AOT. Как насчет некоторых практических доказательств?

Ответ 8

JIT могут идентифицировать и устранять некоторые условия, которые могут быть известны только во время выполнения. Ярким примером является устранение виртуальных вызовов, используемых современными виртуальными машинами - например, когда JVM находит инструкцию invokevirtual или invokeinterface, если только один класс, переопределяющий вызванный метод, был загружен, виртуальная машина может фактически сделать этот виртуальный вызов статическим и, таким образом, может встроить его. С другой стороны, для программы C указатель функции всегда является указателем на функцию, и вызов к нему не может быть встроен (в общем случае, так или иначе).

Здесь ситуация, когда JVM может встроить виртуальный вызов:

interface I { 
    I INSTANCE = Boolean.getBoolean("someCondition")? new A() : new B();
    void doIt(); 
}
class A implements I { 
    void doIt(){ ... } 
}
class B implements I { 
    void doIt(){ ... } 
}
// later...
I.INSTANCE.doIt();

Предполагая, что мы не обходим создание экземпляров A или B в другом месте и что для параметра someCondition установлено значение true, JVM знает, что вызов doIt() всегда означает A.doIt и может поэтому избегайте поиска таблицы методов, а затем встраивайте вызов. Подобная конструкция в среде, отличной от JIT, не была бы проницаемой.

Ответ 9

Я думаю, что значительная часть этого является тот факт, что официальный компилятор Java является JIT-компилятором. Сколько времени было потрачено на оптимизацию JVM против компилятора машинного кода для Java?