+4

Giới thiệu về Java Virtual Machine (JVM)

Mayfest2023

1. Giới thiệu

Java Virtual Machine (JVM) là một thành phần trung tâm của nền tảng Java, đóng vai trò quan trọng trong việc chạy các ứng dụng Java. JVM là một máy ảo được thiết kế để thực thi mã Java dưới dạng bytecode, đảm bảo khả năng di động của mã nguồn Java thông qua mô hình "Viết một lần, chạy ở mọi nơi" (Write Once, Run Anywhere - WORA). Mục đích của JVM là giúp người dùng Java chạy các ứng dụng Java trên nhiều hệ điều hành và phần cứng khác nhau mà không cần phải biên dịch lại mã nguồn. Điều này được thực hiện bằng cách sử dụng một class trừu tượng giữa mã nguồn Java và hệ điều hành, nơi JVM thực thi mã bytecode Java và quản lý bộ nhớ cho các ứng dụng Java. Tầm quan trọng của JVM đối với ngôn ngữ lập trình Java không thể phủ nhận. Hiểu về JVM, cấu trúc của nó, cách hoạt động và vai trò trong việc chạy ứng dụng Java giúp lập trình viên Java có thể tận dụng tối đa khả năng của nền tảng Java, tối ưu hóa hiệu năng của ứng dụng và giải quyết các vấn đề liên quan đến bộ nhớ và hiệu năng.

2. Cấu trúc và thành phần chính của JVM

2.1. Class Loader

Class Loader là thành phần chịu trách nhiệm tải các class Java (class) vào JVM. Class Loader đọc các tệp tin .class chứa bytecode và chuyển chúng thành các đối tượng class trong bộ nhớ heap của JVM. Class Loader được chia thành ba loại:

  • Bootstrap Class Loader: được sử dụng để tải các class chính của Java
  • Extension Class Loader: được sử dụng để tải các class mở rộng của Java
  • Application Class Loader: được sử dụng để tải các class được định nghĩa trong chương trình Java Class Loader trong Java cung cấp tính linh hoạt cho việc tải class, cho phép các class có thể được tải vào JVM từ nhiều nguồn khác nhau, nhưng cũng có thể gây ra các vấn đề liên quan đến việc tải class như tải các phiên bản class khác nhau hoặc xung đột tên class. Quá trình tải class trong JVM bao gồm các giai đoạn:
  • Loading (Tải class): Class Loader tải các tệp tin bytecode của class vào trong bộ nhớ và tạo ra một đối tượng class trong Runtime Data Area.
  • Linking (Liên kết): Class Loader thực hiện các bước liên kết class, bao gồm:
    • Verification (Xác minh): Kiểm tra tính hợp lệ của các tệp tin bytecode của class để đảm bảo rằng chúng tuân thủ các quy tắc của Java.
    • Preparation (Chuẩn bị): Chuẩn bị các cấu trúc dữ liệu cho class, bao gồm các biến static.
    • Resolution (Giải quyết): Tìm kiếm và liên kết các class và các thư viện liên quan đến class được tải.
  • Initialization (Khởi tạo): Thực hiện khởi tạo các giá trị ban đầu cho các biến static và khối static của class.

2.2. Runtime Data Area

Runtime Data Area là vùng bộ nhớ được JVM sử dụng để lưu trữ dữ liệu khi thực thi chương trình. Nó bao gồm các phần sau:

2.2.1. Method Area

Method Area trong JVM là một vùng nhớ được dùng để lưu trữ cấu trúc của các class, bao gồm tên class, thông tin về các trường và phương thức của class, cũng như mã bytecode của các phương thức. Method Area được chia sẻ giữa tất cả các luồng thực thi (thread) trong JVM. Nó là một phần của bộ nhớ được cấp phát cho JVM khi khởi động. Method Area bao gồm một số thành phần sau:

  • Constant Pool: chứa các thông tin liên quan đến hằng số, như số nguyên, chuỗi ký tự, các tham chiếu tới các class, phương thức và trường.
  • Field Area: chứa thông tin về các trường của class, bao gồm tên trường, kiểu dữ liệu và thông tin phạm vi truy cập.
  • Method Area: chứa thông tin về các phương thức của class, bao gồm tên phương thức, kiểu trả về, danh sách tham số, thông tin phạm vi truy cập và mã bytecode của phương thức.
  • Class Area: chứa thông tin về class, bao gồm tên class, thông tin về class cha, danh sách các giao diện mà class hiện tại triển khai, và các thông tin khác về class. Method Area có thể được sử dụng bởi tất cả các luồng thực thi trong JVM để truy cập thông tin về các class và các phương thức được tải vào bộ nhớ. Tuy nhiên, Method Area có thể gặp phải các vấn đề như tràn bộ nhớ hoặc xung đột khi một số class quá lớn hoặc khi nhiều luồng thực thi truy cập đồng thời vào cùng một class.

2.2.2. Heap

Heap trong JVM là một vùng nhớ được dùng để lưu trữ các đối tượng và mảng được khởi tạo bởi chương trình Java. Heap cũng được chia sẻ giữa tất cả các luồng thực thi trong JVM. Khi chương trình Java chạy, nó sử dụng Heap để cấp phát bộ nhớ cho các đối tượng và mảng. Khi một đối tượng được tạo ra trong chương trình, JVM sẽ tìm kiếm một vùng nhớ trống trong Heap và cấp phát bộ nhớ cho đối tượng đó. Heap được quản lý bởi Garbage Collector (GC) trong JVM. GC sẽ quét và thu dọn các đối tượng không sử dụng để giải phóng bộ nhớ Heap đã cấp phát cho các đối tượng này. Việc thu dọn này giúp giảm sự cố tràn bộ nhớ và giúp tăng hiệu suất của chương trình Java. Tuy nhiên, việc sử dụng Heap có thể gặp phải các vấn đề như tràn bộ nhớ, xung đột khi nhiều luồng thực thi truy cập vào cùng một đối tượng trong Heap hoặc khi sử dụng các đối tượng có kích thước lớn. Do đó, cần thiết phải thiết kế và triển khai chương trình Java sao cho sử dụng Heap một cách hiệu quả để giảm thiểu các vấn đề liên quan đến bộ nhớ trong chương trình.

2.2.3. Stack

stack-area-of-jvm Stack trong JVM là một vùng nhớ được sử dụng để lưu trữ các stack frame cho mỗi lần gọi phương thức trong mỗi luồng thực thi của chương trình Java. Mỗi stack frame chứa các biến cục bộ, tham số và kết quả của phương thức đang được gọi. Mỗi luồng thực thi trong JVM có một stack riêng, giúp cho việc thực thi của từng luồng không ảnh hưởng đến các luồng khác. Khi một phương thức được gọi, một stack frame mới sẽ được tạo ra và được đẩy vào stack của luồng thực thi hiện tại. Sau khi phương thức hoàn thành thực thi, stack frame sẽ được loại bỏ khỏi stack của luồng thực thi và trả lại bộ nhớ. Stack cũng được sử dụng để lưu trữ các giá trị trả về của phương thức, các địa chỉ trả về và các thông tin về ngoại lệ (exception). Khi xảy ra một ngoại lệ trong chương trình, JVM sẽ tìm kiếm stack để tìm stack frame nơi ngoại lệ đã xảy ra và thực hiện các xử lý liên quan đến ngoại lệ đó. Tuy nhiên, việc sử dụng quá nhiều bộ nhớ trên Stack cũng có thể dẫn đến tràn bộ nhớ và gây ra các vấn đề liên quan đến hiệu suất của chương trình. Do đó, cần thiết phải thiết kế và triển khai chương trình sao cho sử dụng Stack một cách hiệu quả để giảm thiểu các vấn đề liên quan đến bộ nhớ trong chương trình.

2.2.4. Program Counter (PC) Register

Program Counter (PC) Register trong JVM là một thanh ghi riêng biệt được sử dụng để lưu trữ địa chỉ của mã bytecode đang được thực thi cho mỗi luồng thực thi trong chương trình Java. PC Register giúp cho JVM biết được phần mã bytecode nào đang được thực thi trong chương trình. Khi một phương thức được gọi, giá trị của PC Register sẽ được cập nhật để trỏ đến địa chỉ của bytecode đầu tiên trong phương thức đó. Khi chương trình đang thực thi, giá trị của PC Register sẽ được cập nhật mỗi khi JVM thực hiện một bytecode mới. Khi phương thức kết thúc, giá trị của PC Register sẽ trở về địa chỉ của bytecode tiếp theo sau phương thức gọi. PC Register cũng được sử dụng để quản lý việc thực hiện các câu lệnh nhảy (jump) trong chương trình. Khi một câu lệnh nhảy được thực thi, giá trị của PC Register sẽ được cập nhật để trỏ đến địa chỉ mới của mã bytecode để thực thi. Việc sử dụng PC Register cho phép JVM thực thi mã bytecode một cách hiệu quả và đáng tin cậy. Tuy nhiên, PC Register không được sử dụng để lưu trữ dữ liệu, nó chỉ được sử dụng để lưu trữ địa chỉ của các bytecode đang được thực thi.

2.2.5. Native Method Stack

Native Method Stack trong JVM là một stack riêng biệt được sử dụng cho các phương thức native, tức là các phương thức được viết bằng ngôn ngữ lập trình khác ngoài Java. Native Method Stack được sử dụng để lưu trữ các thông tin cần thiết cho việc thực thi các phương thức native. Khi một phương thức native được gọi, giá trị của PC Register sẽ trỏ đến địa chỉ của phương thức native đó và Native Method Stack sẽ được tạo ra để lưu trữ các thông tin liên quan đến việc thực thi phương thức native. Native Method Stack bao gồm các stack frame chứa các tham số và biến cục bộ của phương thức native. Native Method Stack có tính độc lập với Stack của JVM và được quản lý bởi JVM. Khi phương thức native hoàn thành thực thi, Native Method Stack sẽ được loại bỏ khỏi bộ nhớ. Việc sử dụng Native Method Stack cho phép các phương thức native được thực thi một cách hiệu quả và đáng tin cậy. Tuy nhiên, vì các phương thức native được viết bằng ngôn ngữ lập trình khác ngoài Java, nên việc sử dụng Native Method Stack có thể gặp phải các vấn đề liên quan đến quản lý bộ nhớ và sự tương thích giữa các ngôn ngữ khác nhau.

2.3. Execution Engine

Execution Engine là thành phần chịu trách nhiệm thực thi mã bytecode trong JVM. Nó bao gồm:

2.3.1. Interpreter

Interpreter trong JVM là một thành phần được sử dụng để đọc và thực thi mã bytecode của chương trình Java. Interpreter đọc từng lệnh bytecode một và thực thi lệnh đó trên JVM. Khi chương trình Java được biên dịch, mã nguồn được chuyển đổi sang mã bytecode. Interpreter sẽ đọc các lệnh bytecode này và thực thi chúng một lệnh tại một thời điểm. Interpreter sử dụng các giá trị trên stack để thực thi các phép tính, gán giá trị và các thao tác khác được thực hiện bởi lệnh bytecode. Interpreter giúp cho chương trình Java có thể thực thi trên các nền tảng khác nhau mà không cần phải biên dịch lại chương trình cho từng nền tảng. Tuy nhiên, do Interpreter đọc và thực thi mã bytecode một lệnh tại một thời điểm nên hiệu suất thực thi của chương trình có thể không được tối ưu. Để cải thiện hiệu suất, JVM có thể sử dụng các kỹ thuật khác như Just-In-Time (JIT) Compiler để biên dịch các phần mã bytecode được sử dụng thường xuyên thành mã máy và lưu trữ trong bộ nhớ để sử dụng lại trong lần thực thi tiếp theo.

2.3.2. Just-In-Time (JIT) Compiler

Just-In-Time (JIT) Compiler trong JVM là một công cụ được sử dụng để tối ưu hóa việc thực thi mã bytecode của chương trình Java bằng cách biên dịch các phần mã bytecode thường xuyên được gọi thành mã máy trước khi thực thi. Điều này giúp tăng tốc độ thực thi chương trình và cải thiện hiệu suất của chương trình Java. Khi chương trình Java được thực thi, JIT Compiler sẽ quét và phân tích mã bytecode của chương trình và tìm ra các phần mã bytecode thường xuyên được gọi (hot code). Sau đó, JIT Compiler sẽ biên dịch các phần mã này thành mã máy và lưu trữ vào bộ nhớ để sử dụng lại trong lần thực thi tiếp theo. Việc biên dịch các phần mã bytecode này chỉ được thực hiện khi chương trình đang thực thi và các phần mã này đã được xác định là hot code, nên JIT Compiler được gọi là một công cụ tối ưu hóa động (dynamic optimization). Việc sử dụng JIT Compiler giúp cho chương trình Java có thể thực thi nhanh hơn và tối ưu hóa hiệu suất của chương trình. Tuy nhiên, việc biên dịch các phần mã bytecode này có thể tốn nhiều thời gian và tài nguyên của hệ thống, nên việc sử dụng JIT Compiler cần được cân nhắc kỹ lưỡng để đảm bảo hiệu quả và độ ổn định của chương trình.

2.3.3. Garbage Collector (GC)

Garbage Collector (GC) trong JVM là một thành phần quan trọng được sử dụng để thu gom rác và giải phóng bộ nhớ heap bị chiếm giữ bởi các đối tượng không còn được sử dụng nữa trong chương trình Java. Khi một đối tượng không còn được sử dụng nữa, GC sẽ giải phóng bộ nhớ được sử dụng bởi đối tượng đó và trả lại cho bộ nhớ heap để sử dụng cho các đối tượng khác trong chương trình. GC giúp ngăn chặn tình trạng hết bộ nhớ (out-of-memory) trong JVM, giúp chương trình có thể chạy ổn định và tránh được các lỗi liên quan đến bộ nhớ. GC sẽ chạy một cách tự động trong JVM để giải phóng các đối tượng không còn được sử dụng nữa, và chương trình Java không cần phải quan tâm đến việc quản lý bộ nhớ một cách chi tiết. Các thuật toán dọn dẹp bộ nhớ là những phương pháp mà Garbage Collector (GC) sử dụng để giải phóng bộ nhớ heap trong JVM. Mỗi thuật toán sẽ có những ưu điểm và hạn chế riêng, phù hợp với các tình huống sử dụng khác nhau của chương trình Java. Dưới đây là một số thuật toán dọn dẹp bộ nhớ phổ biến trong JVM:

  • Serial GC: Một trong những thuật toán GC cổ điển nhất và đơn giản nhất trong JVM. Nó sử dụng một luồng (thread) cho việc thu thập rác và dừng toàn bộ ứng dụng trong quá trình thu gom rác
  • Parallel GC: Thuật toán GC sử dụng multiple threads để thu thập rác trên bộ nhớ heap
  • Concurrent Mark Sweep (CMS) GC: Thuật toán thu gom rác được thiết kế để giảm thiểu độ trễ (latency) trong quá trình thu gom rác bằng cách sử dụng nhiều luồng để thực hiện việc thu gom rác
  • Garbage First (G1) GC: Thuật toán GC mới được giới thiệu từ JDK 7u4. G1 GC được thiết kế để giải quyết các vấn đề về hiệu suất và khả năng mở rộng của CMS GC, đồng thời giảm thiểu độ trễ (latency) trong quá trình thu gom rác.
  • Z Garbage Collector (ZGC): Thuật toán thu gom rác của Oracle được giới thiệu trong Java 11. ZGC được thiết kế để hỗ trợ các ứng dụng Java đòi hỏi tính khả dụng cao và thời gian phản hồi thấp.
  • Shenandoah GC: Thuật toán GC mới được giới thiệu vào năm 2018 bởi Oracle. Nó là một thuật toán GC thuộc loại concurrent, có khả năng giảm thiểu độ trễ và tối ưu hóa hiệu suất của ứng dụng. Tuy nhiên, việc sử dụng GC cũng có thể ảnh hưởng đến hiệu suất của chương trình nếu GC được thực hiện quá thường xuyên hoặc không đủ thường xuyên. Do đó, việc thiết kế và triển khai chương trình sao cho sử dụng GC một cách hiệu quả và tối ưu là rất quan trọng để đảm bảo hiệu quả và độ ổn định của chương trình.

3. Cách JVM hoạt động và vai trò của nó trong việc chạy ứng dụng Java

JVM đóng vai trò quan trọng trong việc chạy ứng dụng Java. Các bước chính trong quá trình này bao gồm:

3.1. Quá trình khởi động JVM

Khi bạn chạy một ứng dụng Java, Java Virtual Machine (JVM) được khởi động bởi Java Runtime Environment (JRE). Quá trình khởi động JVM đi qua các bước sau: Khi chạy một ứng dụng Java, Java Virtual Machine (JVM) được khởi động bởi Java Development Kit (JDK) hoặc Java Runtime Environment (JRE). JDK hoặc JRE bao gồm các thành phần cần thiết để chạy các chương trình Java, bao gồm JVM, các thư viện và các công cụ hỗ trợ. Tuy nhiên, từ Java 11 trở đi, JRE đã bị loại bỏ khỏi JDK và chỉ có thể được tải xuống và cài đặt riêng lẻ.

3.3.1. Khởi tạo JVM

Khi chạy một chương trình Java trên JDK, JDK sẽ sử dụng jlink để tạo ra một file image chứa các module cần thiết để thực thi chương trình Java. Trong quá trình khởi động, JVM sẽ tải các module được định nghĩa trong image để thực thi chương trình. Các thông số và cấu hình JVM có thể được thiết lập bằng cách sử dụng các tùy chọn dòng lệnh hoặc thông qua các tệp cấu hình được cung cấp trong file cấu hình Java (Java configuration file). Một số thông số và cấu hình quan trọng của JVM bao gồm:

  • Kích thước bộ nhớ heap: Đây là kích thước của bộ nhớ heap được phân bổ cho chương trình Java.
  • Các thông số thu gom rác: Các thông số này quy định cách thu gom rác được thực hiện trong JVM, bao gồm tần suất thu gom rác và phương pháp thu gom rác được sử dụng.
  • Các thông số thời gian chạy: Các thông số này quy định các thời gian chạy của JVM, bao gồm thời gian chờ đợi trước khi bắt đầu thu gom rác và thời gian chờ đợi trước khi kết thúc chương trình.
  • Các thông số quản lý bộ nhớ: Các thông số này quy định cách quản lý bộ nhớ trong JVM, bao gồm cách phân bổ bộ nhớ và cách xử lý các tài nguyên bộ nhớ. Các thông số và cấu hình này có thể được tùy chỉnh để đáp ứng các yêu cầu và điều kiện khác nhau của chương trình Java.

3.3.2. Tải class khởi động

Sau khi JVM được khởi tạo, nó sẽ bắt đầu quá trình tải các class Java. Quá trình tải class được thực hiện bởi Class Loader và diễn ra trong các giai đoạn: loading, linking và initialization (chi tiết tại phần Class Loader). Class khởi động (Bootstrap Class) là class đầu tiên được tải vào trong JVM. Đây là class chứa các class cốt lõi của Java, bao gồm các class trong gói java.lang, java.utiljava.io. Sau khi class khởi động đã được tải vào trong JVM, JVM sẽ tải chính class ứng dụng được chỉ định bởi người dùng khi chạy ứng dụng Java. class ứng dụng chứa phương thức main() và là điểm khởi đầu cho quá trình thực thi của chương trình Java.

3.3.3. Khởi tạo class ứng dụng

Sau khi tải class ứng dụng, JVM sẽ khởi tạo class đó bằng cách gọi phương thức static main() của class ứng dụng. Phương thứcmain() sẽ được thực thi đầu tiên khi chương trình Java được khởi động và nó sẽ là điểm bắt đầu cho luồng thực thi của chương trình.

3.2. Quá trình thực thi mã của ứng dụng

Sau khi hoàn tất quá trình khởi động JVM, việc thực thi mã của ứng dụng bắt đầu. Dưới đây là các bước chính trong quá trình thực thi mã của ứng dụng:

3.2.1. Tải và liên kết các class

Trong quá trình thực thi mã của ứng dụng, JVM cần tải và liên kết các class ứng dụng. Việc tải class (class loading) là quá trình đọc tệp .class từ hệ thống tệp hoặc từ mạng, phân tích cấu trúc của tệp và tạo ra biểu diễn trong bộ nhớ của class. JVM sử dụng các Class Loader để thực hiện việc tải class (chi tiết tại phần Class Loader).. Các class ứng dụng thường được tải bằng Application Class Loader. Class Loader này tìm kiếm các tệp .class trong CLASSPATH của ứng dụng. Nếu không tìm thấy trong CLASSPATH, Class Loader sẽ tìm kiếm trong các thư viện của ứng dụng hoặc mạng.

3.2.2. Khởi tạo các class và tạo ra các đối tượng

Sau khi tải và liên kết các class, JVM sẽ tiến hành khởi tạo các class và tạo ra các đối tượng của chúng. Quá trình khởi tạo class bao gồm việc gán giá trị ban đầu cho các trường tĩnh và thực hiện các khối khởi tạo tĩnh (static initialization blocks) của class. Các khối khởi tạo tĩnh này được thực hiện chỉ một lần khi class được tải và trước khi bất kì đối tượng nào của class đó được tạo ra. Sau khi class đã được khởi tạo, JVM sẽ cấp phát bộ nhớ cho các đối tượng của class đó và gọi các hàm khởi tạo (constructors) tương ứng để khởi tạo các đối tượng. Các hàm khởi tạo này sẽ thực hiện các thao tác để khởi tạo các biến thành viên (instance variables) của đối tượng và gán giá trị ban đầu cho chúng. Sau khi các đối tượng được khởi tạo, chúng sẽ được lưu trữ trong bộ nhớ heap và có thể được sử dụng trong quá trình thực thi của chương trình.

3.2.3. Thực thi mã của ứng dụng

Cuối cùng, JVM sẽ bắt đầu thực thi mã bytecode của ứng dụng bằng cách thực thi các phương thức của các class đã tải và khởi tạo. Đầu tiên, JVM sẽ gọi phương thức main của class chính của ứng dụng để bắt đầu quá trình thực thi. Trong quá trình thực thi, JVM có thể gọi các phương thức khác của cùng một class hoặc của các class khác nhau, tùy thuộc vào luồng thực thi của ứng dụng. JVM sử dụng bộ đếm chương trình (program counter) để theo dõi lệnh bytecode đang được thực thi và sử dụng ngăn xếp (stack) để lưu trữ các giá trị trung gian và các khung ngăn xếp (stack frame) cho các phương thức đang được gọi. Nếu cần, JVM sẽ sử dụng Just-In-Time (JIT) Compiler để tối ưu hóa hiệu suất thực thi của chương trình.

3.2.4. Quản lý bộ nhớ và thu gom rác (Garbage Collection)

Khi thực thi mã của ứng dụng, JVM quản lý việc cấp phát và giải phóng bộ nhớ cho các đối tượng. Bộ nhớ được cấp phát cho các đối tượng trong heap, và JVM sử dụng thuật toán thu gom rác (garbage collection) để tự động giải phóng bộ nhớ của các đối tượng không còn được sử dụng nữa.

3.2.5. Kết thúc ứng dụng

Quá trình thực thi của ứng dụng sẽ kết thúc khi JVM gặp một trong các trường hợp sau:

  • Phương thức main hoặc một phương thức khác đang chạy kết thúc bằng cách gọi hàm System.exit() hoặc các phương thức tương đương.
  • Tất cả các thread chính của ứng dụng đã kết thúc.
  • JVM gặp một lỗi nghiêm trọng hoặc một ngoại lệ không được xử lý và buộc phải dừng lại.

Khi chương trình kết thúc thực thi, JVM sẽ giải phóng tất cả các tài nguyên được sử dụng bởi chương trình và kết thúc quá trình thực thi. JVM sẽ thực hiện các hoạt động giải phóng tài nguyên, bao gồm:

  • Giải phóng bộ nhớ heap: JVM sẽ giải phóng tất cả các đối tượng và mảng được tạo ra trong bộ nhớ heap của JVM.
  • Giải phóng bộ nhớ stack: JVM sẽ giải phóng tất cả các stack frame được tạo ra trong bộ nhớ stack của JVM.
  • Giải phóng các tài nguyên khác: JVM sẽ giải phóng tất cả các tài nguyên khác được sử dụng bởi chương trình Java, bao gồm các tài nguyên I/O và các kết nối mạng. Sau khi tất cả các tài nguyên đã được giải phóng, JVM sẽ kết thúc quá trình thực thi và trả về trạng thái ban đầu. Quá trình này đảm bảo rằng các tài nguyên được sử dụng bởi chương trình Java sẽ không tiếp tục chiếm giữ bộ nhớ và tài nguyên của hệ thống sau khi chương trình đã kết thúc.

4. Kết luận

Trong bài viết này, chúng ta đã tìm hiểu về Java Virtual Machine (JVM), một thành phần quan trọng trong việc chạy ứng dụng Java. Chúng ta đã khám phá cấu trúc và thành phần chính của JVM, bao gồm Class Loader, Runtime Data Areas, Execution Engine và Native Interface. Chúng ta cũng đã đi sâu vào cách JVM hoạt động và vai trò của nó trong việc khởi động, thực thi mã và kết thúc ứng dụng Java. Hiểu rõ hơn về JVM sẽ giúp các lập trình viên Java phát triển và tối ưu hóa ứng dụng hiệu quả hơn, đồng thời tìm hiểu sâu hơn về ngôn ngữ Java và nền tảng Java.

5. Tài liệu tham khảo

  1. Oracle. (2021). The Java® Virtual Machine Specification, Java SE 17 Edition. Oracle. https://docs.oracle.com/javase/specs/jvms/se17/html/index.html
  2. Gosling, J., Joy, B., Steele, G., Bracha, G., & Buckley, A. (2014). The Java® Language Specification, Java SE 8 Edition. Oracle. https://docs.oracle.com/javase/specs/jls/se8/html/index.html
  3. Java Virtual Machine (JVM) Stack Area. GeeksforGeeks. https://www.geeksforgeeks.org/java-virtual-machine-jvm-stack-area/
  4. Understanding the Java Virtual Machine: Class loading and Reflection. Pluralsight. https://www.pluralsight.com/guides/java/java-virtual-machine-class-loading-and-reflection
  5. Inside the Java Virtual Machine. Artima Developer. https://www.artima.com/insidejvm/ed2/
  6. JVM Architecture: Understanding JVM Internals. DZone. https://dzone.com/articles/jvm-architecture-explained
  7. _How Java Virtual Machine (JVM) Works?. Java Code Geeks. https://examples.javacodegeeks.com/core-java/how-java-virtual-machine-jvm-works/

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.