Существует много общего между обеими реализациями (и, на мой взгляд, да, они обе "виртуальные машины").
С одной стороны, они обе являются виртуальными машинами на основе стека, без понятия «регистры», как мы привыкли видеть в современных процессорах, таких как x86 или PowerPC. Оценка всех выражений ((1 + 1) / 2) выполняется путем помещения операндов в «стек» и последующего извлечения этих операндов из стека, когда инструкция (сложение, деление и т. Д.) Должна использовать эти операнды. Каждая инструкция помещает свои результаты обратно в стек.
Это удобный способ реализации виртуальной машины, потому что почти каждый ЦП в мире имеет стек, но число регистров часто различается (и некоторые регистры имеют специальное назначение, и каждая инструкция ожидает свои операнды в разных регистры и т. д.).
Так что, если вы собираетесь моделировать абстрактную машину, то модель, основанная исключительно на стеках, - хороший путь.
Конечно, реальные машины не работают таким образом. Таким образом, JIT-компилятор отвечает за выполнение «регистрации» операций с байт-кодом, по существу, планируя фактические регистры ЦП, чтобы они по возможности содержали операнды и результаты.
Итак, я думаю, что это одно из самых больших сходств между CLR и JVM.
Что касается различий ...
Одно интересное различие между двумя реализациями состоит в том, что CLR включает инструкции для создания универсальных типов, а затем для применения параметрических специализаций к этим типам. Таким образом, во время выполнения CLR считает List совершенно другим типом из List .
Под прикрытием он использует один и тот же MSIL для всех специализаций ссылочного типа (таким образом, список использует ту же реализацию, что и список , с различными приведениями типов на границах API), но каждое значение -type использует собственную уникальную реализацию (List генерирует совершенно другой код из List ).
В Java универсальные типы - это чисто трюк с компилятором. JVM не имеет представления о том, какие классы имеют аргументы типа, и не может выполнять параметрические специализации во время выполнения.
С практической точки зрения это означает, что вы не можете перегружать методы Java для универсальных типов. У вас не может быть двух разных методов с одинаковым именем, различающихся только тем, принимают ли они List или List . Конечно, поскольку CLR знает о параметрических типах, у него нет проблем с обработкой методов, перегруженных в специализациях универсальных типов.
В повседневной жизни я больше всего замечаю разницу между CLR и
JVM.
Другие важные отличия включают в себя:
CLR имеет замыкания (реализовано как делегаты C #). JVM поддерживает замыкания только после Java 8.
В CLR есть сопрограммы (реализовано с помощью C # ключевого слова yield). JVM этого не делает.
CLR позволяет пользовательскому коду определять новые типы значений (структуры), тогда как JVM предоставляет фиксированный набор типов значений (байтовый, короткий, int, long, float, double, char, boolean) и разрешает только пользователи могут определять новые ссылочные типы (классы).
CLR поддерживает декларирование и манипулирование указателями. Это особенно интересно, потому что как JVM, так и CLR используют строгие реализации сжатия сборщиков мусора в качестве стратегии управления памятью. При обычных обстоятельствах строгому сжатию GC действительно трудно справиться с указателями, потому что при перемещении значения из одной ячейки памяти в другую все указатели (и указатели на указатели) становятся недействительными. Но CLR предоставляет механизм «закрепления», так что разработчики могут объявить блок кода, в котором CLR не разрешено перемещать определенные указатели. Это очень удобно.
Самая большая единица кода в JVM - это либо «пакет», о чем свидетельствует ключевое слово «защищенный», либо, возможно, JAR (то есть Java ARchive), что подтверждается возможностью указать jar в пути к классам и Рассматривать это как папку с кодом. В CLR классы объединяются в «сборки», а CLR предоставляет логику для рассуждения о сборках и манипулирования ими (которые загружаются в «домены приложений», предоставляя «песочницы» на уровне приложений для выделения памяти и выполнения кода).
Формат байт-кода CLR (составленный из инструкций и метаданных MSIL) имеет меньше типов инструкций, чем JVM. В JVM каждая уникальная операция (добавление двух значений int, добавление двух значений с плавающей запятой и т. Д.) Имеет собственную уникальную инструкцию. В CLR все инструкции MSIL являются полиморфными (добавьте два значения), и JIT-компилятор отвечает за определение типов операндов и создание соответствующего машинного кода. Хотя я не знаю, какая стратегия предпочтительнее. У обоих есть компромиссы. JIT-компилятор HotSpot для JVM может использовать более простой механизм генерации кода (ему не нужно определять типы операндов, потому что они уже закодированы в инструкции), но это означает, что ему нужен более сложный формат байт-кода, с большим количеством типов инструкций.
Я использую Java (и восхищаюсь JVM) уже около десяти лет.
Но, на мой взгляд, CLR теперь является превосходной реализацией почти во всех отношениях.