Что такое runtime языка

Среды времени выполнения

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

WriteLn() в Pascal или malloc() в C.

Т.е. для программиста это данность. Конечно, вполне очевидно, что для работы инструмента должны быть какие-то базовые, примитивные вещи, и это очевидно. Они и составляют среду времени выполнения или по английски runtime.

Я же предлагаю посмотреть на это несколько под другим углом.

Вот этот набор базовых, примитивных “кирпичиков” составляет некий исполнительный механизм, с которым программист взаимодействует. Этот механизм представляет собой самостоятельную программную сущность. И при таком подходе можно выделять свойства этого механизма.

Самостоятельность

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

Напротив, среда времени выполнения языков C и C++ представляет собой набор библиотек, лежащих на диске и загружаемых по мере использования (в случае с DLL/SO), либо зашиваемых внутрь исполняемого файла.

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

Уровни абстракции

Языки бывают разного уровня абстракции. То же самое относится и к средам выполнения. Это значит, что на единицу информации в среде приходится разное количество полезных действий. Разумеется, одна операция на языке 1С будет существенно отличаться от операции на C.

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

Обычно лучше всего это получается для простых сред вроде C. Для более сложных сред приходится либо вкладывать одну среду внутрь другой, например при встраивании языка Lua внутрь программы на C++, либо использовать другие механизмы объединения вроде Foreign Function Interface.

Соглашения о вызовах

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

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

Так, в благодаря хорошему пониманию устройства сред выполнения появляются проекты вроде Clasp, объединяющем среды выполнения C++ и Common Lisp.


Иногда среду времени выполнения можно подменить полностью или частично. Так, например, механизм выделения памяти в C можно заменить, скажем, на jemalloc. То же самое касается и других частей среды.

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

Написано 07.11.2018