Я думаю, вы все уже знаете, что Java — это независимый от платформы язык. Это одно из ключевых преимуществ Java. Чтобы понять, как Java достигает независимости от платформы, нам сначала нужно понять, как компиляция работает под капотом. Я постараюсь объяснить это, насколько мне известно.

Что такое компилятор:

Компьютеры могут понимать только машинный язык. Если вы не знаете, как выглядит машинный язык, то это пример машинного кода для вас :).

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

Итак, что вы думаете, вы уже начали стучать по нашей клавиатуре, чтобы написать свой следующий большой проект на языке ассемблера. Я знаю, я знаю, что это даже не смешно. Так что, возможно, вы, как и я, тоже не хотите писать на ассемблере. Вот почему некоторые выдающиеся люди среди нас изобрели языки программирования высокого уровня, такие как C, C++ и Java, которые гораздо более удобочитаемы для человека. Так что я думаю, вы уже знаете, что компьютер, конечно, не понимает эти языки высокого уровня. Компиляторы в помощь.

Компиляторы могут преобразовать ваш высокоуровневый код (с этого момента мы будем использовать для этого термин исходный код) в любой желаемый целевой код. Мы используем термин целевой код вместо машинного кода, потому что компиляторы могут превращать исходный код не только в машинный код. Например, компилятор C превратит ваш исходный код в машинный код. Но компилятор Java превратит ваш исходный код в байт-код. Мы также можем использовать компилятор для преобразования одного исходного кода в другой исходный код.

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

  1. Lexing — Разбейте текст программы на «токены».
  2. Синтаксический анализ. Преобразование этой последовательности токенов в дерево синтаксического анализа.
  3. Оптимизация — оценивайте константные выражения, оптимизируйте неиспользуемые переменные или недостижимый код и т. д.
  4. Перевод — перевод дерева синтаксического анализа на машинный язык.

Так что этот процесс компиляции занимает некоторое время в зависимости от компиляторов и работы, которую они выполняют.

Теперь представьте, что у вас есть операционная система Windows и вы хотите запустить на своем компьютере программу hello-world.c. Во-первых, вам нужно скомпилировать его с помощью компилятора C, который превратит ваш исходный код в машинный код. Он создаст файл hello-world.exe, также известный как исполняемый файл. Теперь вы можете запустить программу. Теперь представьте, что ваш друг использует Linux, и он также хочет запустить эту программу hello-world.c на своем компьютере. у него есть два варианта: вы можете отправить ему свой hello-word.exe или он должен скомпилировать код с помощью компилятора на своей машине. Вы можете попробовать первый подход, он не сработает. Потому что процессор, на котором выполняется машинный код, должен быть аналогичен процессору, на котором генерируется машинный код. У вашего друга нет другого выбора, кроме как перекомпилировать этот файл hello-world.c на своем компьютере с помощью своего машинного компилятора. Итак, как мы можем удалить эту машинную зависимость?

Устный переводчик:

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

Интерпретатор делает это по-другому. Сам интерпретатор — это программа, непосредственно исполняющая исходный код. Для интерпретируемых языков нет отдельного шага компиляции. Таким образом, между процессором и интерпретатором есть сходство. ЦП может преобразовать машинный код в результаты, а интерпретатор может преобразовать исходный код в результаты.

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

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

Хотя Interpreter дает нам независимость от платформы, и это круто. Но у него есть существенный недостаток. Интерпретируемые языки намного медленнее, чем компилируемые языки. Причин несколько, основные из них - дорогое обращение к памяти и каждый раз переинтерпретируется исходный код.

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

Java пытается дать вам лучшее из обоих миров. Как?

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

Вы можете подумать, что это интерпретируемый язык и он не быстрый. Но позвольте мне заверить вас, что это быстро :). Интерпретация байт-кода Java намного быстрее, чем интерпретация исходного кода любого другого языка, поскольку он скомпилирован и оптимизирован. Java также использует другую технику для дальнейшего повышения производительности, называемую компиляцией JIT. Это своего рода механизм кэширования вашего часто исполняемого байт-кода, также известного как «горячие точки».