Основная особенность Java, которая позволяет решать описанные ранее проблемы обеспечения безопасности и переносимости программ, состоит в том, что компилятор Java выдает не исполняемый код, а так называемый байт-код — в высшей степени оптимизированный набор инструкций, предназначенных для выполнения в исполняющей системе Java, называемой виртуальной машиной Java (Java Virtual Machine — JVM). Собственно говоря, первоначальная версия виртуальной машины JVM разрабатывалась в качестве интерпретатора байт-кода. Это может вызывать недоумение, поскольку для обеспечения максимальной производительности компиляторы многих современных языков программирования призваны создавать исполняемый код. Но то, что программа на Java интерпретируется виртуальной машиной JVM, как раз помогает решить основные проблемы разработки программ для Интернета. И вот почему.
Трансляция программы Java в байт-код значительно упрощает ее выполнение в разнотипных средах, поскольку на каждой платформе необходимо реализовать только виртуальную машину JVM. Если в отдельной системе имеется исполняющий пакет, в ней можно выполнять любую программу на Java. Следует, однако, иметь в виду, что все виртуальные машины JVM на разных платформах, несмотря на некоторые отличия и особенности их реализации, способны правильно интерпретировать один и тот же байт-код. Если бы программа на Java компилировалась в машинозависимый код, то для каждого типа процессоров, подключенных к Интернету, должны были бы существовать отдельные версии одной и той же программы. Ясно, что такое решение неприемлемо. Таким образом, организация выполнения байт-кода виртуальной машиной JVM — простейший способ создания по-настоящему переносимых программ.
Тот факт, что программа на Java выполняется виртуальной машиной JVM, способствует также повышению ее безопасности. Виртуальная машина JVM управляет выполнением программы, поэтому она может изолировать программу и воспрепятствовать возникновению побочных эффектов от ее выполнения за пределами данной системы. Как станет ясно в дальнейшем, ряд ограничений, существующих в языке Java, также способствует повышению безопасности.
В общем, когда программа компилируется в промежуточную форму, а затем интерпретируется виртуальной машиной JVM, она выполняется медленнее, чем если бы она была скомпилирована в исполняемый код. Но в Java это отличие в производительности не слишком заметно. Байт-код существенно оптимизирован, и поэтому его применение позволяет виртуальной машине JVM выполнять программы значительно быстрее, чем следовало ожидать.
Язык Java был задуман как интерпретируемый, но ничто не препятствует ему оперативно выполнять компиляцию байт-кода в машинозависимый код для повышения производительности. Поэтому вскоре после выпуска Java появилась технология HotSpot, которая предоставляет динамический компилятор (или так называемый JIT-компилятор) байт-кода. Если динамический компилятор входит в состав виртуальной машины JVM, то избранные фрагменты байт-кода компилируются в исполняемый код по частям, в реальном времени и по требованию. Важно понимать, что одновременная компиляция всей программы Java в исполняемый код нецелесообразна, пocкoлькyJava производит различные проверки, которые могут быть сделаны только во время выполнения. Вместо этого динамический компилятор компилирует код во время выполнения по мере надобности. Более того, компилируются не все фрагменты байт-кода, а только те, которым компиляция принесет выгоду, а остальной код просто интерпретируется. Тем не менее принцип динамической компиляции обеспечивает значительное повышение производительности. Даже при динамической компиляции байт-кода характеристики переносимости и безопасности сохраняются, поскольку виртуальная машина JVM по-прежнему отвечает за целостность исполняющей среды.