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

Почему JVM не компилирует всю программу спереди, а не компилирует ее по частям?

В этой теме Герберт Шильдт пишет:

Важно понимать, что практически невозможно полностью скомпилировать всю программу Java в исполняемый код, поскольку Java выполняет различные проверки во время выполнения, которые могут выполняться только во время выполнения.

Какие проверки времени выполнения он имеет в виду?

Пожалуйста, объясните причины компиляции байт-кода по частям, а не всей программы.

4b9b3361

Ответ 1

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

Во-первых, неизвестно, насколько велика программа. Люди могли бы расстроиться с 30-минутным запуском, поскольку он скомпилировал каждую найденную библиотеку (данная Java-программа не находится в одном файле, она имеет доступ ко всему в пути к классам)

Для другого, даже если вы сказали системе точно, какие компоненты будут использовать ваши программы, нет никакой информации о том, какая часть вашей программы может быть использована для данного запуска - люди будут более расстроены при 30-минутном запуске для запуска программа командной строки с параметрами, состоящими из "--help"

Наконец, он может сделать некоторые отличные трюки, компилируя во время его работы. с таким способом:

public testMeh(int param) {
    if(param == 35) 
        do bunches of crap;
    else if (param > 5)
        do much more crap;
    else 
        return;
    }

Компилятор может вызывать его один или два раза и "на лету" распознавать значения 5 и только при возврате. Если это вызывается все время со значением 2, оно может заменить вызов метода ENTIRE с помощью if (param!= 2) testMeh (param);

который устраняет весь вызов метода для этого числа. Позже он может понять, что не вызов этого метода означает, что некоторые переменные-члены не могут меняться и которые могут свернуть другие части кода до нуля.

Это просто так сложно, если вы прекомпилируете материал. Я имею в виду, что везде можно писать код исключения, поскольку вы распознаете шаблоны, но ваш код быстро станет кошмаром.

Теперь, если вы спрашиваете, почему бы не прекомпилировать всю программу при компиляции ее в байт-код - это другой вопрос, а не то, что котировка обращалась вообще, но вы можете это сделать. Это было сделано и работает очень хорошо. Вы быстрее переносите переносимость и гибкость во время выполнения.

Ответ 2

Может быть несколько причин, чтобы скомпилировать его по частям (это первые два, которые мне приходят):

  • Оптимизация кода, который используется много раз, не весь код необходимо перекомпилировать, а только конкретную часть.
  • Приобретение классов по сети - иногда вы хотите избежать приобретения всего кода, поскольку он стоит в полосе пропускания.

И я не знаю, является ли этот сайт точным, но я многому научился у него: http://www.artima.com/insidejvm/ed2/index.html

Ответ 3

Это рассуждение неверно. Можно полностью скомпилировать всю программу Java в исполняемый код, и фактически gcj делает именно это.

Фактические причины, по которым Java-среды выполнения обычно не выполняют предварительную компиляцию:

  • Как часть Java "писать один раз, запускать где угодно", скомпилированный код распространяется как независимый от платформы байт-код вместо собственного кода на основе платформы.

  • Компиляция байт-кода на собственный код при запуске является неоптимальным по нескольким причинам. Во-первых, это повлечет за собой довольно внушительную начальную стоимость, особенно для более крупных программ. Кроме того, современные JIT могут использовать профилирование во время выполнения, чтобы обеспечить лучшую оптимизацию, чем это было бы возможно с помощью переднего компилятора. Оптимизация обычно включает в себя компромисс: например, inlining удаляет накладные расходы функциональных вызовов за счет большего кода (что, в свою очередь, может привести к более медленному коду из-за плохой производительности кэша или даже замены). Когда у вас есть информация о профилировании во время компиляции, вы можете более разумно решить, какие компромиссы имеют смысл.

  • API Java имеет историю, основанную на возможности загрузки байт-кода во время выполнения. (Подумайте о том, что полагается на Class.forName и/или пользовательские загрузчики классов, например, включая апплеты, контейнеры сервлетов и т.д.). Для поддержки этого необходим какой-то компилятор (или интерпретатор) во время выполнения. Выполнение передовой компиляции означало бы, что вам придется либо удалить поддержку для этого, либо рассматривать его как особый случай.

Ответ 4

Одна из возможных причин: Java runtime - очень динамичная среда. Классы загружаются и выгружаются все время. В зависимости от используемых в настоящее время классов определенные оптимизации возможны или невозможны. Например, если у вас есть интерфейс A и единая реализация ImplA, все вызовы методов в интерфейсе A могут быть изменены для использования прямых вызовов методам ImplA, без использования каких-либо виртуальных методов. После загрузки другого класса AnotherImplA такая оптимизация невозможна. После того, как AnotherImplA будет выгружен, это возможно снова.

В JVM много оптимизаций, которые используют данные, собранные во время выполнения. Выполнение всех оптимизаций не позволит многим возможностям.

Проверьте также недавний разговор от Cliff Click, JVM Does That?

Ответ 5

Более быстрое время запуска и возможность более разумной оптимизации после профилирования кода во время выполнения.

В этой статье дается отличный ответ на этот вопрос: https://advancedweb.hu/2016/05/27/jvm_jit_optimization_techniques/

JVM начинает интерпретацию всего на лету. Он отслеживает, сколько раз вызывается функция, и если она проходит определенный предел, она компилирует эту функцию и кэширует этот машинный код. Каждый раз после вызова этой функции выполняется этот машинный код. Если он проходит другой порог, он может быть скомпилирован снова, но выполняется еще более агрессивная оптимизация.

Цель состоит в том, чтобы сбалансировать предварительную стоимость компиляции. Компиляция всех фронтов приведет к более длительному времени запуска, и компилятор не сможет сделать оптимальную оптимизацию, не увидев запуск кода. Более длинные запущенные программы могут улучшить оптимизацию, чем более короткие запущенные программы.

Он также может скомпилировать только тело цикла, если он выполняется столько раз.