Có rất nhiều điểm tương đồng giữa cả hai triển khai (và theo tôi: có, cả hai đều là "máy ảo").
Đối với một điều, cả hai đều là VM dựa trên ngăn xếp, không có khái niệm "thanh ghi" như chúng ta thường thấy trong một CPU hiện đại như x86 hoặc PowerPC. Việc đánh giá tất cả các biểu thức ((1 + 1) / 2) được thực hiện bằng cách đẩy các toán hạng lên "ngăn xếp" và sau đó bật các toán hạng đó ra khỏi ngăn xếp bất cứ khi nào một lệnh (thêm, chia, v.v.) cần tiêu thụ các toán hạng đó. Mỗi hướng dẫn đẩy kết quả của nó trở lại vào ngăn xếp.
Đây là một cách thuận tiện để triển khai máy ảo, vì hầu hết mọi CPU trên thế giới đều có một ngăn xếp, nhưng số lượng thanh ghi thường khác nhau (và một số thanh ghi là mục đích đặc biệt và mỗi lệnh đều mong đợi toán hạng của nó trong các thanh ghi khác nhau, v.v. ).
Vì vậy, nếu bạn định tạo mô hình một cỗ máy trừu tượng, một mô hình hoàn toàn dựa trên ngăn xếp là một cách khá hay.
Tất nhiên, máy thật không hoạt động theo cách đó. Vì vậy, trình biên dịch JIT chịu trách nhiệm thực hiện "đăng ký" các hoạt động của mã byte, về cơ bản lập lịch trình các thanh ghi CPU thực tế để chứa các toán hạng và kết quả bất cứ khi nào có thể.
Vì vậy, tôi nghĩ đó là một trong những điểm tương đồng lớn nhất giữa CLR và JVM.
Còn về sự khác biệt ...
Một sự khác biệt thú vị giữa hai triển khai là CLR bao gồm các hướng dẫn để tạo các loại chung và sau đó để áp dụng các chuyên ngành tham số cho các loại đó. Vì vậy, trong thời gian chạy, CLR coi Danh sách <int> là một loại hoàn toàn khác với Danh sách <Chuỗi>.
Trong các trang bìa, nó sử dụng cùng một MSIL cho tất cả các chuyên môn kiểu tham chiếu (do đó, Danh sách <Chuỗi> sử dụng cùng cách thực hiện với Danh sách <Đối tượng>, với các kiểu phôi khác nhau ở ranh giới API), nhưng mỗi loại sử dụng loại giá trị triển khai duy nhất của riêng nó (Danh sách <int> tạo mã hoàn toàn khác với Danh sách <double>).
Trong Java, các kiểu chung hoàn toàn là một thủ thuật biên dịch. JVM không có khái niệm về các lớp nào có đối số kiểu và nó không thể thực hiện các chuyên môn tham số khi chạy.
Từ góc độ thực tế, điều đó có nghĩa là bạn không thể quá tải các phương thức Java trên các kiểu chung. Bạn không thể có hai phương thức khác nhau, có cùng tên, chỉ khác nhau về việc chúng có chấp nhận Danh sách <Chuỗi> hay Danh sách <Ngày>. Tất nhiên, vì CLR biết về các loại tham số, nên không có phương thức xử lý vấn đề nào bị quá tải đối với các chuyên ngành loại chung.
Trên cơ sở hàng ngày, đó là sự khác biệt mà tôi nhận thấy nhiều nhất giữa CLR và JVM.
Sự khác biệt quan trọng khác bao gồm:
CLR đã đóng cửa (được triển khai dưới dạng đại biểu C #). JVM chỉ hỗ trợ các bao đóng kể từ Java 8.
CLR có coroutines (được triển khai với từ khóa C #'ield '). JVM thì không.
CLR cho phép mã người dùng xác định các loại giá trị mới (structs), trong khi JVM cung cấp một tập hợp cố định các loại giá trị (byte, short, int, long, float, double, char, boolean) và chỉ cho phép người dùng xác định tham chiếu mới- các loại (lớp).
CLR cung cấp hỗ trợ cho việc khai báo và thao tác con trỏ. Điều này đặc biệt thú vị bởi vì cả JVM và CLR đều sử dụng các triển khai thu gom rác nén thế hệ nghiêm ngặt như là chiến lược quản lý bộ nhớ của họ. Trong các trường hợp thông thường, một GC nén chặt có một thời gian thực sự khó khăn với các con trỏ, bởi vì khi bạn di chuyển một giá trị từ vị trí bộ nhớ này sang vị trí bộ nhớ khác, tất cả các con trỏ (và con trỏ đến con trỏ) đều không hợp lệ. Nhưng CLR cung cấp một cơ chế "ghim" để các nhà phát triển có thể khai báo một khối mã trong đó CLR không được phép di chuyển một số con trỏ nhất định. Nó rất tiện lợi.
Đơn vị mã lớn nhất trong JVM là 'gói' được chứng minh bằng từ khóa 'được bảo vệ' hoặc được cho là JAR (tức là Java ARchive) được chứng minh bằng cách có thể chỉ định một jar trong đường dẫn lớp và được xử lý như một thư mục mã. Trong CLR, các lớp được tổng hợp thành 'cụm' và CLR cung cấp logic để suy luận và thao tác các cụm (được tải vào "AppDomains", cung cấp các hộp cát cấp ứng dụng phụ để cấp phát bộ nhớ và thực thi mã).
Định dạng mã byte CLR (bao gồm các lệnh MSIL và siêu dữ liệu) có ít loại lệnh hơn JVM. Trong JVM, mọi hoạt động duy nhất (thêm hai giá trị int, thêm hai giá trị float, v.v.) có hướng dẫn riêng của nó. Trong CLR, tất cả các lệnh MSIL là đa hình (thêm hai giá trị) và trình biên dịch JIT chịu trách nhiệm xác định các loại toán hạng và tạo mã máy thích hợp. Tôi không biết đó là chiến lược tốt nhất, mặc dù. Cả hai đều có sự đánh đổi. Trình biên dịch JIT HotSpot, đối với JVM, có thể sử dụng cơ chế tạo mã đơn giản hơn (không cần xác định các loại toán hạng, vì chúng đã được mã hóa trong hướng dẫn), nhưng điều đó có nghĩa là nó cần định dạng mã byte phức tạp hơn, với nhiều loại hướng dẫn hơn.
Tôi đã sử dụng Java (và ngưỡng mộ JVM) khoảng mười năm nay.
Nhưng, theo tôi, CLR hiện là sự triển khai vượt trội, gần như trên mọi phương diện.