Віртуальна машина Java та CLR


140

Як свого роду наступне запитання під назвою " Відмінності між MSIL та байт-кодом Java?" , в чому полягають (основні) відмінності або схожість у тому, як працює віртуальна машина Java порівняно з принципом роботи.NET Framework Загальна мовна робота (CLR) працює?

Крім того, є .NET Framework CLR - "віртуальна машина" чи у неї немає атрибутів віртуальної машини?


Що ж, якщо ви порівнюєте подібне та подібне, вам слід переформулювати питання, як різниця між VM та CLR (Common Language Runtime), що є прямим аналогом VM.
клент

Відповіді:


278

Є багато подібності між обома реалізаціями (і на мою думку: так, вони обидва "віртуальні машини").

По-перше, вони обидва 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 зараз є найкращим впровадженням, майже в усіх відношеннях.


73
Закриття та генератори реалізовані на мовному рівні та просто представлені у вигляді класів на рівні CLR.
Керт Хагенлохер

2
Як щодо відмінностей у тому, як вони обробляють купу? CLR більше залежить від операційної системи / хоста, тоді як JVM більш-менш повністю управляє купою пам'яті.
Келлі С. Французька

6
Важливою відмінністю є контраст між компіляцією щойно вчасно (CLR) та адаптивною оптимізацією в (Oracle / Sun) JVM.
Едвін Далорцо

1
Локальні змінні слоти Java діють подібно до регістрів. Але все одно суперечить, оскільки JIT перетворює локальні слоти та стеки в справжні регістри.
Сурма

1
@kuhajeyan це тому, що коли було запроваджено CLR, JVM було 10 років. це довго в ІТ. Коли JVM прийшов у 1993 році, не було серйозних претендентів, для CLR (2003) з'явився зрілий і міцний JVM з сильними опорами в промисловості.
Простий товариш

25

Ваше перше питання - це порівняння JVM з .NET Framework - я припускаю, що ви насправді мали намір порівняти з CLR. Якщо так, я думаю, що ви можете написати про це невелику книгу ( EDIT: схоже, у Benji вже є :-)

Важлива відмінність полягає в тому, що CLR розроблений таким, що не є мовою, а архітектура на відміну від JVM.

Ще одна важлива відмінність полягає в тому, що CLR був розроблений спеціально для забезпечення високого рівня сумісності з власним кодом. Це означає, що CLR повинен керувати надійністю та захищеністю при доступі та модифікованій нативній пам'яті, а також керувати розподілом між структурами даних на основі CLR та нативними структурами даних.

Щоб відповісти на ваше друге запитання, термін "віртуальна машина" - це більш старий термін із світу апаратних засобів (наприклад, віртуалізація IBM в 360-х роках 1960-х років), який раніше означав програмну / апаратну емуляцію базової машини для досягнення того ж типу речі, які робить VMWare.

CLR часто називають "двигуном виконання". У цьому контексті це реалізація IL-машини на версії x86. Це також те, що робить JVM, хоча ви можете стверджувати, що є важлива різниця між поліморфними байткодами CLR та типовими байткодами JVM.

Тож педантична відповідь на ваше друге запитання - «ні». Але це дійсно зводиться до того, як ви визначаєте ці два терміни.

EDIT: Ще одна відмінність між JVM та CLR полягає в тому, що JVM (версія 6) дуже неохоче відпускає виділену пам'ять назад в операційну систему, навіть там, де це можливо.

Наприклад, скажімо, що процес JVM запускається і виділяє спочатку 25 Мб пам'яті з операційної системи. Код програми потім намагається виділити, що потребує додаткових 50 Мб. JVM виділить додаткові 50 Мб з операційної системи. Після того, як код програми перестане використовувати цю пам'ять, він збирається сміттям і розмір купи JVM зменшиться. Однак JVM звільнить виділену операційну пам'ять лише за певних конкретних обставин . В іншому випадку протягом останнього періоду життя ця пам'ять залишатиметься виділеною.

З іншого боку, CLR викидає виділену пам'ять назад в операційну систему, якщо вона більше не потрібна. У наведеному вище прикладі CLR звільнив би пам'ять, якби зменшилася купа.


2
Це абсолютно не правильно, що JVM не звільнить виділену пам'ять. Дивіться мою відповідь на це запитання для підтвердження: stackoverflow.com/questions/366658/…
Michael Borgwardt

Я бачив повернення пам'яті JVM до Windows.
Стів Куо

Я змінив свою відповідь, сказавши, що JVM 6 дуже неохоче звільняє пам'ять, посилаючись на відповіді Рана та Майкла. Я ніколи не бачив такої поведінки з JVM 5, тому, можливо, ця версія була ще більш неохочею.
HTTP 410

Чи можете ви пояснити, як JVM активно управляє купою, поки CLR покладається на батьківський процес? Конкретним прикладом, який я використовую, є те, що JVM має аргументи виконання для максимального розміру купи, тоді як середовище CLR за замовчуванням не робить. Хоча це правда, програма CLR, розміщена під IIS, може налаштувати IIS для обмеження пам'яті, це означатиме включення IIS у визначення віртуальної машини.
Келлі С. Французька

@Steve Kuo, так, я теж це бачив. зазвичай між 17:00 та 18:00.
Простий товариш

11

CLR і JVM - це віртуальні машини.

.NET Framework та середовище виконання Java - це група відповідних віртуальних машин та їх бібліотек. Без бібліотек VM досить марні.


11

Більш детальну інформацію про відмінності можна знайти в різних академічних та приватних джерелах. Колись хороший приклад - вибір CLR дизайну .

Деякі конкретні приклади включають:

  • Деякі операнди низького рівня набираються, наприклад, "додати два входи", коли CLR використовує поліморфний операнд. (тобто fadd / iadd / ladd vs просто додати)
  • В даний час JVM проводить більш агресивне профілювання та оптимізацію виконання (тобто Hotspot). CLR в даний час робить оптимізацію JIT, але не оптимізацію виконання (тобто замінює код під час роботи).
  • CLR не вбудовує віртуальні методи, JVM робить ...
  • Підтримка типів значень у CLR виходить за рамки лише "примітивів".

-11

Це не віртуальна машина, .net Framework збирає збірки в нативну бінарну під час першого запуску:

Під час обчислень, щойно-вчасно компіляція (JIT), також відома як динамічний переклад, - це методика покращення продуктивності виконання комп'ютерної програми. JIT спирається на дві попередні ідеї в середовищах виконання: компіляція байтових кодів та динамічна компіляція. Він перетворює код під час виконання перед його початковим виконанням, наприклад, байт-код у нативний машинний код. Поліпшення продуктивності в порівнянні з інтерпретаторами виникає з кешування результатів перекладу блоків коду, а не просто переоцінки кожного рядка або операнду кожного разу, коли воно досягається (див. Інтерпретована мова). Він також має переваги перед статичним складанням коду на час розробки, оскільки він може перекомпілювати код, якщо це визнається вигідним, і, можливо, зможе застосувати гарантії безпеки.

Кілька сучасних середовищ виконання, такі як .NET Framework Microsoft, більшість реалізацій Java та останнім часом Actioncript 3, покладаються на компіляцію JIT для швидкого виконання коду.

Джерело: http://en.wikipedia.org/wiki/Just-in-time_compilation

Додавання .NET Framework містить віртуальну машину, як і Java.


10
Тільки тому, що віртуальна машина використовує JIT для оптимізації продуктивності, це ще не означає, що це вже не віртуальна машина. Коли програміст компілює, він збирається у віртуальну машину, залишаючи до реалізації для виконання виконання, але це вважає за потрібне
Аллан Лалонде
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.