Các ứng dụng Java sẽ chạy trên một máy ảo Java (JVM) nên việc hiểu cách thức các ứng dụng Java vận hành trong JVM như thế nào sẽ giúp chúng ta biết cách tổ chức và viết code một cách hiệu quả hơn. Hiện nay, chúng ta có nhiều loại JVM khác nhau do các công ty khác nhau xây dựng dựa vào JVM specification như: HotSpot JVM (Oracle), JRockit (Oracle), Jamiga (dành cho nền tảng AmigaOS),… nhưng phổ biến nhất vẫn là HotSpot JVM do Oracle xây dựng. Trong bài viết này, mình sẽ trình bày với các bạn về các thành phần của loại máy ảo này!
Các thành phần chính của HotSpot JVM bao gồm: VM Runtime, JIT Compiler và Garbage Collector.
VM Runtime
VM Runtime là thành phần chính cung cấp các service và các API thông dụng cho JIT Compiler và Garbage Collector. Nó cũng cung cấp các chức năng cơ bản của HotSpot JVM như một bộ khởi động máy ảo, quản lý thread, Java Native Interface, …
Các chức năng chính của VM Runtime cụ thể bao gồm:
Phân tích các argument mà chúng ta truyền vào khi khởi động máy ảo
Khi khởi động máy ảo, chắc chắn chúng ta sẽ có nhiều option kèm theo và VM Runtime sẽ có nhiệm vụ phân tích các option này và cấu hình HotSpot JVM dựa vào những option này.
Việc cấu hình này chắc chắn sẽ ảnh hưởng đến hiệu suất của HotSpot JVM. Có 3 category cho các command line option, đó là:
- Standard Options: đây là những option mà tất cả các JVM đều phải có, định nghĩa bởi JVM specification.
- Nonstandard Options: là những option bắt đầu với ký tự “-X”. Tất nhiên những option này sẽ không đảm bảo là sẽ có ở tất cả các JVM.
- Developer Options: là những option bắt đầu với ký tự “-XX”. Những option này sẽ specific cho từng system.
Các bạn có thể xem thêm các thông tin về HotSpot JVM option tại đây.
Quản lý vòng đời của máy ảo từ lúc khởi động cho đến lúc kết thúc hoạt động.
Ở đây chúng ta có nhiều công cụ khởi động máy ảo như: chạy chương trình bằng command java, chạy ứng dụng Java Web Start sử dụng command javaws,..
VM Class Loading.
VM Runtime sẽ chịu trách nhiệm trong việc load các đối tượng vào bộ nhớ để thực thi với nhiều cách khác nhau như: Class.forName(), ClassLoader.loadClass(), …
Xác minh bytecode
Để đảm bảo rằng, tập tin sau khi compile .class được compile bởi một command javac tin cậy, Java sẽ có cơ chế để xác minh lại bytecode. Việc xác minh này sẽ dựa vào rất nhiều constraint trong bytecode các bạn nhé!
Trình thông dịch
Dựa vào một bảng gọi là TemplateTable chứa mã máy tương ứng với từng bytecode cho từng hệ điều hành, VM Runtime sẽ thông dịch từng bytecode sang mã máy để chương trình của chúng ta có thể thực thi được trên hệ điều hành đó.
Xử lý ngoại lệ
Bất kỳ một exception nào xảy ra, VM Runtime sẽ chịu trách nhiệm thông báo exception đó đến cho người dùng.
JIT Compiler
Việc thông dịch từng bytecode sang mã máy sẽ mất rất nhiều thời gian. Những đoạn mã giống nhau nếu cứ phải lặp đi lặp lại việc thông dịch với chúng thì sẽ không hiệu quả. Để cải tiến điều này, khái niệm JIT Compiler đã được giới thiệu.
Đây là thành phần dùng để thông dịch những bytecode giống nhau trong chương trình của chúng ta sang mã máy. VM Runtime sẽ phân tích các đoạn code được thực thi nhiều lần để chuyển cho JIT Compiler thực hiện việc thông dịch sang mã máy.
Garbage Collector
Trong một ứng dụng Java, các đối tượng Java sẽ được tạo ra rất nhiều. Một trong số chúng được sử dụng xuyên suốt trong vòng đời của ứng dụng từ lúc chạy nó cho đến khi kết thúc nó. Và sẽ có những đối tượng Java được tạo ra chỉ để dùng trong chốc lát và sẽ không được dùng nữa. Nếu cứ để những đối tượng không được sử dụng nữa tồn tại trong bộ nhớ thì chắc chắn không lâu sau, bộ nhớ sẽ bị đầy và chương trình cũng không thể chạy được nữa.
Để giải quyết vấn đề này, những đối tượng không được sử dụng nữa phải được giải phóng ra khỏi bộ nhớ. Đối với các ngôn ngữ như C C++ thì việc giải phóng bộ nhớ sẽ thực hiện thủ công bằng cách sử dụng các đoạn code. Nhưng đối với Java thì việc này sẽ xảy ra hoàn toàn tự động thông qua quá trình gọi là Garbage Collection.
Qúa trình Garbage Collection được thực hiện bởi các Garbage Collector.