Lịch sử hình thành Java Virtual Machine
Vào đầu những năm 1991, Dự án “Green” do James Gosling dẫn dắt đã đặt ra mục tiêu phát triển một kiến trúc chương trình cho phép chạy trên nhiều nền tảng khác nhau. Ban đầu, nhóm phát triển dự định sử dụng ngôn ngữ lập trình C++, nhưng nhanh chóng nhận ra rằng C++ không đáp ứng được yêu cầu về khả năng chạy đa nền tảng. Cụ thể, một tệp thực thi như Hello.exe biên dịch trên hệ thống Windows không thể chạy trực tiếp trên Linux. Mặc dù C++ rất phổ biến, nhưng tính không tương thích đã khiến nhóm phải tìm kiếm giải pháp khác.
Điều đặt ra là làm thế nào để chạy mã trên nhiều nền tảng khác nhau? Giải pháp đầu tiên được nghĩ đến là sử dụng trình thông dịch. Mỗi khi một dòng mã được thực thi, mã máy sẽ được tạo ra và thực thi. Các ngôn ngữ như Python và Ruby đã thành công trong việc này bằng cách yêu cầu cài đặt một trình thông dịch trên mỗi hệ điều hành, từ đó có thể đạt được mục tiêu đa nền tảng.
Tuy nhiên, các trình thông dịch vẫn tồn tại nhược điểm lớn, đó là khả năng tối ưu hóa thấp hơn so với các trình biên dịch, làm cho chương trình chạy chậm hơn. Vậy làm thế nào để khắc phục vấn đề này?
Giải pháp được đưa ra là kết hợp giữa trình biên dịch và trình thông dịch. Trình biên dịch sẽ chuyển đổi mã Java thành mã byte, trong khi Java Virtual Machine (JVM) sẽ chuyển mã byte đó thành mã máy cụ thể cho nền tảng đang sử dụng. Trong quá trình chuyển đổi, JVM có thể thực hiện các thao tác tối ưu hóa thông qua công nghệ JIT (Just-In-Time).
Kiến trúc của Java Virtual Machine
JVM được cấu thành từ ba phần chính:
Class Loader
Đây là thành phần cao nhất trong cấu trúc của JVM, có chức năng tải các lớp và tệp .class. Nếu các tệp này không được tải thành công, khu vực dữ liệu Runtime và quá trình thực thi cũng sẽ không hoạt động. Class Loader chịu trách nhiệm đưa các tệp bytecode vào bộ nhớ thông qua ba giai đoạn: Loading (tải), Linking (liên kết) và Initialization (khởi tạo).
Runtime Data Area
Khu vực này quản lý tài nguyên để thực thi bytecode. Nếu không còn tài nguyên nào, ngoại lệ OutOfMemoryError sẽ được phát sinh. JVM xác định cách phân bổ bộ nhớ trong suốt quá trình chạy chương trình Java. Nói một cách đơn giản, khu vực dữ liệu (data area) lưu trữ thông tin và dữ liệu bytecode trong suốt quá trình thực thi. Trình Garbage Collector (GC) sẽ thực hiện thu gom các đối tượng không còn cần thiết trong miền nhớ này.
Execution Engine
Execution Engine thực hiện các nhiệm vụ cụ thể liên quan đến việc chạy mã. Khái niệm “máy ảo” và “máy vật lý” thường đi đôi với nhau; cả hai đều có khả năng thực thi mã, nhưng cách thức thực hiện lại khác nhau. Execution Engine chuyển đổi mã byte thành các lệnh máy cục bộ tương ứng. Mục tiêu chính của Execution Engine là biên dịch mã byte thành mã máy cho nền tảng hiện tại.
- Interpreter: Đọc bytecode và thực hiện từng chỉ dẫn. Mặc dù có thể diễn giải mã byte nhanh chóng, nhưng phương pháp này thường chậm hơn so với việc biên dịch.
- JIT Compiler: Thực hiện biên dịch có chọn lọc, chuyển đổi một số bytecode thành mã cục bộ, giúp tăng tốc độ thực thi đáng kể.
- Garbage Collector: Quản lý việc thu gom các đối tượng không còn cần thiết trong bộ nhớ Heap.
Tổng kết
Tóm lại, Java Virtual Machine (JVM) là một môi trường quan trọng trong việc thực thi các chương trình Java. JVM không chỉ giúp giấu đi những phức tạp của hệ điều hành và phần cứng mà còn cung cấp một nền tảng ổn định, an toàn và đồng nhất cho các ứng dụng. Khi chúng ta ném các tệp bytecode vào JVM, chương trình có thể chạy mượt mà trên bất kỳ hệ điều hành nào như Windows, Linux hay MacOS, mà không cần lo lắng về sự khác biệt giữa các nền tảng.
source: viblo