Ada banyak kesamaan antara kedua implementasi (dan menurut saya: ya, mereka berdua "mesin virtual").
Untuk satu hal, keduanya VM berbasis stack, tanpa gagasan "register" seperti yang biasa kita lihat di CPU modern seperti x86 atau PowerPC. Evaluasi semua ekspresi ((1 + 1) / 2) dilakukan dengan mendorong operan ke "tumpukan" dan kemudian mengeluarkan operan tersebut dari tumpukan kapan pun instruksi (tambah, bagi, dll) perlu mengonsumsi operan tersebut. Setiap instruksi mendorong hasilnya kembali ke tumpukan.
Ini adalah cara yang mudah untuk mengimplementasikan mesin virtual, karena hampir semua CPU di dunia memiliki setumpuk, tetapi jumlah register sering berbeda (dan beberapa register memiliki tujuan khusus, dan setiap instruksi mengharapkan operandnya dalam register yang berbeda, dll. ).
Jadi, jika Anda akan memodelkan mesin abstrak, model berbasis stack murni adalah cara yang cukup baik untuk dilakukan.
Tentu saja, mesin nyata tidak beroperasi seperti itu. Jadi kompiler JIT bertanggung jawab untuk melakukan "enregistrasi" dari operasi bytecode, pada dasarnya menjadwalkan register CPU aktual untuk berisi operan dan hasil bila memungkinkan.
Jadi, saya pikir itu adalah salah satu kesamaan terbesar antara CLR dan JVM.
Adapun perbedaan ...
Satu perbedaan yang menarik antara kedua implementasi adalah bahwa CLR mencakup instruksi untuk membuat tipe generik, dan kemudian untuk menerapkan spesialisasi parametrik untuk tipe tersebut. Jadi, pada saat runtime, CLR menganggap Daftar <int> sebagai tipe yang sama sekali berbeda dari Daftar <String>.
Di bawah sampul, ia menggunakan MSIL yang sama untuk semua spesialisasi tipe referensi (jadi Daftar <String> menggunakan implementasi yang sama dengan Daftar <Object>, dengan berbagai tipe-gips di batas-batas API), tetapi setiap tipe nilai menggunakan implementasi uniknya sendiri (Daftar <int> menghasilkan kode yang sangat berbeda dari Daftar <double>).
Di Jawa, tipe generik adalah trik kompiler murni. JVM tidak memiliki gagasan tentang kelas mana yang memiliki tipe-argumen, dan itu tidak dapat melakukan spesialisasi parametrik saat runtime.
Dari perspektif praktis, itu berarti Anda tidak bisa membebani metode Java pada tipe generik. Anda tidak dapat memiliki dua metode berbeda, dengan nama yang sama, hanya berbeda pada apakah mereka menerima Daftar <String> atau Daftar <Tanggal>. Tentu saja, karena CLR tahu tentang tipe parametrik, ia tidak memiliki masalah metode penanganan kelebihan beban pada spesialisasi tipe generik.
Pada basis sehari-hari, itulah perbedaan yang paling saya perhatikan antara CLR dan JVM.
Perbedaan penting lainnya termasuk:
CLR memiliki penutupan (diimplementasikan sebagai delegasi C #). JVM tidak mendukung penutupan hanya sejak Java 8.
CLR memiliki coroutine (diimplementasikan dengan kata kunci 'hasil' C #). JVM tidak.
CLR memungkinkan kode pengguna untuk mendefinisikan tipe nilai baru (struct), sedangkan JVM menyediakan koleksi tetap dari tipe nilai (byte, pendek, int, panjang, float, ganda, char, boolean) dan hanya memungkinkan pengguna untuk menentukan referensi baru- jenis (kelas).
CLR memberikan dukungan untuk mendeklarasikan dan memanipulasi pointer. Ini sangat menarik karena JVM dan CLR menggunakan implementasi pengumpul sampah pemadatan generasi yang ketat sebagai strategi manajemen memori mereka. Dalam keadaan biasa, GC pemadatan yang ketat memiliki waktu yang sangat sulit dengan pointer, karena ketika Anda memindahkan nilai dari satu lokasi memori ke yang lain, semua pointer (dan pointer ke pointer) menjadi tidak valid. Tetapi CLR menyediakan mekanisme "pinning" sehingga pengembang dapat mendeklarasikan blok kode di mana CLR tidak diizinkan untuk memindahkan pointer tertentu. Sangat nyaman.
Unit kode terbesar di JVM adalah 'paket' yang dibuktikan dengan kata kunci 'terproteksi' atau bisa dibilang sebagai JAR (yaitu Java ARchive) yang dibuktikan dengan kemampuan menentukan botol di classpath dan memperlakukannya seperti folder kode. Di CLR, kelas digabungkan menjadi 'majelis', dan CLR memberikan logika untuk penalaran dan memanipulasi majelis (yang dimuat ke "AppDomains", menyediakan kotak pasir sub-aplikasi-tingkat untuk alokasi memori dan eksekusi kode).
Format bytecode CLR (terdiri dari instruksi MSIL dan metadata) memiliki tipe instruksi yang lebih sedikit daripada JVM. Dalam JVM, setiap operasi unik (tambahkan dua nilai int, tambahkan dua nilai float, dll) memiliki instruksi uniknya sendiri. Dalam CLR, semua instruksi MSIL bersifat polimorfik (menambahkan dua nilai) dan kompiler JIT bertanggung jawab untuk menentukan jenis operan dan membuat kode mesin yang sesuai. Saya tidak tahu strategi mana yang lebih disukai. Keduanya memiliki trade-off. Kompiler JIT HotSpot, untuk JVM, dapat menggunakan mekanisme pembuatan kode yang lebih sederhana (tidak perlu menentukan tipe operan, karena mereka sudah dikodekan dalam instruksi), tetapi itu berarti membutuhkan format bytecode yang lebih kompleks, dengan lebih banyak jenis instruksi.
Saya telah menggunakan Java (dan mengagumi JVM) selama sekitar sepuluh tahun sekarang.
Tapi, menurut saya, CLR sekarang merupakan implementasi yang unggul, dalam hampir setiap cara.