Є багато подібності між обома реалізаціями (і на мою думку: так, вони обидва "віртуальні машини").
По-перше, вони обидва VM на основі стека, не мають поняття "регістри", як ми звикли бачити в сучасному процесорі, як x86 або PowerPC. Оцінювання всіх виразів ((1 + 1) / 2) виконується натисканням операндів на "стек", а потім вискакуванням цих операндів зі стека, коли інструкція (додавання, ділення тощо) потребує споживання цих операндів. Кожна інструкція штовхає свої результати назад на стек.
Це зручний спосіб реалізації віртуальної машини, тому що майже кожен процесор у світі має стек, але кількість регістрів часто відрізняється (а деякі регістри мають спеціальне призначення, і кожна інструкція очікує, що її операнди будуть в різних регістрах тощо) ).
Отже, якщо ви збираєтеся моделювати абстрактну машину, модель з чистою стекою - це досить хороший спосіб.
Звичайно, справжні машини не працюють таким чином. Таким чином, компілятор JIT несе відповідальність за здійснення "реєстрації" операцій байт-кодів, по суті, планування фактичних регістрів процесора, щоб вони містили операнди та результати, коли це можливо.
Отже, я вважаю, що це одна з найбільших спільності між CLR та JVM.
Щодо відмінностей ...
Одна цікава відмінність між двома реалізаціями полягає в тому, що CLR включає в себе інструкції щодо створення загальних типів, а потім щодо застосування параметричних спеціалізацій до цих типів. Отже, під час виконання CLR вважає Список <int> абсолютно іншим типом, ніж Список <String>.
Під обкладинками він використовує один і той же MSIL для всіх спеціалізацій посилальних типів (тому список <String> використовує таку ж реалізацію, як і список <Object>, з різними кастами типів на межах API), але кожен тип значення використовує власна унікальна реалізація (Список <int> генерує зовсім інший код від List <double>).
У Java загальні типи - суто хитрість компілятора. JVM не має поняття, які класи мають аргументи типу, і він не може виконувати параметричні спеціалізації під час виконання.
З практичної точки зору це означає, що ви не можете перевантажувати методи Java на загальні типи. У вас не може бути двох різних методів з тим самим іменем, які відрізняються лише тим, приймають вони список <String> або список <дата>. Звичайно, оскільки CLR знає про параметричні типи, він не має проблем з методами обробки, перевантаженими на спеціалізації загального типу.
Щоденно, це різниця, яку я найбільше помічаю між CLR та JVM.
Інші важливі відмінності включають:
CLR має закриття (реалізується як делегати C #). JVM підтримує закриття лише з Java 8.
CLR має підпрограми (реалізовані за допомогою ключового слова C # 'урожай'). JVM цього не робить.
CLR дозволяє коду користувача визначати нові типи значень (структури), тоді як JVM забезпечує фіксовану колекцію типів значень (байт, короткий, int, довгий, плаваючий, подвійний, char, булевий) і дозволяє лише користувачам визначати нові посилання- види (класи).
CLR забезпечує підтримку декларування та маніпулювання покажчиками. Це особливо цікаво, оскільки як JVM, так і CLR застосовують суворі генерації компактних реалізацій сміттєзбірників як свою стратегію управління пам'яттю. За звичайних обставин, строгий ущільнюючий GC справді важкий час з покажчиками, тому що при переміщенні значення з одного місця пам'яті в інше всі вказівники (і вказівники на покажчики) стають недійсними. Але CLR забезпечує механізм "закріплення", щоб розробники могли оголосити блок коду, в межах якого CLR не може переміщувати певні покажчики. Це дуже зручно.
Найбільша одиниця коду в JVM - це або "пакунок", про що свідчить "захищене" ключове слово, або, мабуть, JAR (тобто Java ARchive), про що свідчить, маючи можливість вказати jar на classpath і обробляти його як папку коду. У CLR класи об’єднуються у "збірки", і CLR надає логіку для міркування про та маніпулювання збірками (які завантажуються в "AppDomains", забезпечуючи пісочниці на рівні додатка для розподілу пам'яті та виконання коду).
Формат байт-кодів CLR (складається з інструкцій та метаданих MSIL) має менше типів інструкцій, ніж JVM. У JVM кожна унікальна операція (додайте два значення int, додайте два значення float тощо) має свою унікальну інструкцію. У CLR всі інструкції MSIL є поліморфними (додайте два значення), а компілятор JIT відповідає за визначення типів операндів та створення відповідного машинного коду. Я не знаю, яка це стратегія бажано. В обох є компроміси. Компілятор JS HotSpot для JVM може використовувати простіший механізм генерації коду (йому не потрібно визначати типи операндів, оскільки вони вже закодовані в інструкції), але це означає, що йому потрібен складніший формат байт-коду, з більш типами інструкцій
Я використовую Java (і захоплююся JVM) вже близько десяти років.
Але, на мою думку, CLR зараз є найкращим впровадженням, майже в усіх відношеннях.