К вашему сведению: эта статья посвящена виртуальным машинам (или средам выполнения), а не системным виртуальным машинам.

С тех пор, как я начал разрабатывать около 2 лет назад, я усвоил много уроков и принципов. Одним из наиболее важных было следующее:

Пытаясь понять сложную концепцию программного обеспечения, начните с его истории. Знание того, почему что-то было создано, делает понимание того, что это, бесконечно проще.

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

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

Краткая история…

До того, как появилась идея виртуальных машин, переносимость кода была большой проблемой. Это было связано с различными существующими аппаратными стандартами. Микропроцессоры (ЦП), например, производятся многими разными компаниями (Intel, AMD, Micron, NVIDIA и т. д.), которые могут использовать или не использовать разные архитектуры (см. здесь).

Из-за этого инженерам приходилось создавать компиляторы для каждой перестановки языка высокого уровня и базовой аппаратной архитектуры. Чтобы избежать этого, была создана концепция разделения процесса компиляции на два этапа (внешний и внутренний) и в конечном итоге популяризированная в 1970-х годах с помощью языка Pascal.

Преимущества двухэтапной компиляции

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

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

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

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

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

Так что же такое виртуальная машина?

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

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

ВМ в реальном мире

Одним из лучших примеров современной виртуальной машины является Common Language Runtime (CLR), который использует промежуточный язык, называемый Common Intermediate Language или CIL (ранее называвшийся Microsoft Intermediate Language). Он реализован и включен в Microsoft .NET Framework.

Существует множество языков, скомпилированных в CIL, включая C#, C++, F# и Visual Basic. Таким образом, если на вашем компьютере установлен .NET, вы можете писать программы на любом из этих языков и запускать их в .NET.

Другим примером является виртуальная машина Java, также называемая средой выполнения Java (JRE). Он использует промежуточный язык, называемый «байт-кодом», в который компилируются несколько других языков, отличных от Java (Clojure, Scala и другие).

Сводка

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

PS: Отличная книга, которая помогла мне более подробно разобраться в виртуальных машинах и компиляции, — Элементы вычислительных систем. Очень рекомендую!

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

Я надеюсь, что вы нашли эту статью полезной! Если да, оставьте несколько хлопков и поделитесь с друзьями.

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

Спасибо,

Скотт