Khi cài Java, bạn sẽ gặp ngay ba chữ viết tắt: JVM, JRE, JDK. Tài liệu nào cũng nhắc, nhưng ít nơi giải thích rõ cái nào làm gì và khi nào dùng cái nào.
Bài này làm rõ ba khái niệm đó — từ analogy dễ nhớ đến kiến trúc bên trong để bạn không còn mù mờ khi đọc docs hay cấu hình server.
1. Analogy — Xe hơi, động cơ, và xưởng sửa xe
Hình dung chiếc xe hơi:
- Động cơ — trái tim của xe. Không có động cơ thì xe chỉ là khung sắt. Động cơ biến xăng thành chuyển động.
- Xe hơi hoàn chỉnh = động cơ + xăng + ghế + điều hoà + đèn + bánh... Bạn ngồi vào và lái được mà không cần hiểu bên trong động cơ hoạt động thế nào.
- Xưởng sửa xe = xe hoàn chỉnh + bộ công cụ (cờ lê, máy chẩn đoán, phụ tùng thay thế...). Thợ máy sửa được và nâng cấp được xe.
Ánh xạ sang Java:
| Xe hơi | Java |
|---|---|
| Động cơ | JVM — chạy bytecode |
| Xe hoàn chỉnh (lái được) | JRE = JVM + thư viện chuẩn |
| Xưởng sửa xe (làm được xe mới) | JDK = JRE + compiler + debugger + tools |
💡 💡 Cách nhớ
- JVM: động cơ — chạy bytecode, không có gì khác
- JRE: xe lái được — JVM + thư viện Java chuẩn (java.util, java.io...) đủ để chạy app
- JDK: xưởng sửa xe — JRE +
javac(compiler) +javap(disassembler) +jdb(debugger) + nhiều tool khác, đủ để viết và build app
2. Định nghĩa — ba cái một lần
2.1 JVM — Java Virtual Machine
JVM là chương trình phần mềm đọc và thực thi bytecode (file .class). Nó là "máy ảo" vì nó mô phỏng một CPU ảo với tập lệnh riêng (bytecode instruction set).
JVM làm ba việc chính:
- Load file
.classvào bộ nhớ - Verify bytecode (kiểm tra không có lệnh nguy hiểm)
- Execute bytecode — interpret hoặc JIT-compile thành native machine code
JVM không phải một thứ duy nhất: mỗi OS (Windows, macOS, Linux) có implementation JVM khác nhau, viết bằng C++. Đây là lý do bytecode portable còn JVM thì không.
2.2 JRE — Java Runtime Environment
JRE = JVM + Java Class Library (thư viện chuẩn Java).
Thư viện chuẩn bao gồm hàng nghìn class bạn dùng hàng ngày:
java.lang— String, Integer, Math, System, Thread...java.util— ArrayList, HashMap, Collections, Optional...java.io/java.nio— đọc/ghi file, streamsjava.net— HTTP, socket, URLjava.time— LocalDate, ZonedDateTime (từ Java 8)
Trước Java 9: JRE là download riêng biệt, nhẹ hơn JDK. Từ Java 9+: JRE không còn là bản phân phối riêng nữa — Oracle không release JRE standalone. Bạn dùng JDK hoặc tạo custom runtime bằng jlink.
2.3 JDK — Java Development Kit
JDK = JRE + development tools. Những tool quan trọng nhất:
| Tool | Lệnh | Dùng để |
|---|---|---|
| Compiler | javac | Compile .java sang .class |
| Launcher | java | Chạy app từ .class hoặc .jar |
| Archiver | jar | Đóng gói thành .jar file |
| Disassembler | javap | Xem bytecode của .class |
| Debugger | jdb | Debug chương trình đang chạy |
| Documentation | javadoc | Generate HTML docs từ Javadoc comments |
| Profiler | jconsole, jvisualvm | Monitor JVM heap, threads, GC |
| Key tool | keytool | Quản lý certificates, keystores |
3. Quan hệ bao nhau
Ba thứ này không ngang hàng — chúng lồng nhau:
flowchart TD jdk["JDK<br/>(Java Development Kit)"] jre["JRE<br/>(Java Runtime Environment)"] jvm["JVM<br/>(Java Virtual Machine)"] tools["javac + jar + javap + jdb + javadoc + ..."] stdlib["Java Class Library<br/>(java.lang, java.util, java.io, java.net ...)"] engine["Bytecode Execution Engine<br/>(ClassLoader + Verifier + JIT + GC)"] jdk --> tools jdk --> jre jre --> stdlib jre --> jvm jvm --> engine
Nói cách khác: JDK ⊃ JRE ⊃ JVM.
4. Khi nào dùng gì?
| Bạn là | Cần gì | Lý do |
|---|---|---|
| Developer viết code Java | JDK | Cần javac để compile, jdb để debug |
| End-user chạy app Java (desktop) | JRE (hoặc JDK) | Chỉ cần chạy, không cần compile |
| Production server chạy app | JDK hoặc JRE | JDK để có thêm monitoring tools; JRE đủ nếu chỉ run |
| CI/CD pipeline build + test | JDK | Cần compile và chạy test |
Docker container chạy .jar | JRE hoặc custom runtime (jlink) | Tối ưu size, chỉ include module cần dùng |
💡 💡 Quy tắc thực tế
Ngày nay, developer luôn cài JDK. Câu hỏi "JRE hay JDK" chủ yếu liên quan đến production server và Docker image size. Với Docker, nhiều team dùng jlink để tạo minimal JRE chỉ chứa modules ứng dụng cần — giảm image từ ~300MB xuống còn ~50-80MB.
5. Kiến trúc bên trong JVM
Hiểu kiến trúc JVM giúp bạn đọc được stack trace, hiểu OutOfMemoryError, và tuning GC về sau. Đây là cái nhìn tổng quan — mỗi component sẽ có bài riêng sâu hơn.
flowchart TD
src["HelloWorld.class<br/>(bytecode input)"]
subgraph jvm_box["JVM"]
cl["ClassLoader<br/>Load + Link + Initialize"]
bv["Bytecode Verifier<br/>Security check"]
subgraph ee["Execution Engine"]
interp["Interpreter<br/>(first run -- slow)"]
jit["JIT Compiler<br/>(hot code -- native speed)"]
end
subgraph rda["Runtime Data Area"]
heap["Heap<br/>(objects, arrays)"]
stack["JVM Stacks<br/>(frames per thread)"]
meta["Metaspace<br/>(class metadata)"]
pc["PC Registers"]
end
gc["Garbage Collector<br/>(G1, ZGC, Shenandoah ...)"]
end
src --> cl --> bv --> ee
ee --> rda
gc -.-> heap5.1 ClassLoader — tải và khởi tạo class
ClassLoader là component đầu tiên, làm 3 việc theo thứ tự:
- Loading — đọc file
.classtừ disk/network, tạoClassobject trong memory - Linking — verify bytecode, cấp phát bộ nhớ static, resolve symbolic references
- Initialization — chạy static initializer block, gán giá trị static field
Java có 3 ClassLoader mặc định theo cấp bậc:
- Bootstrap ClassLoader — load
java.lang,java.util... (built-in, viết bằng C++) - Platform ClassLoader (trước Java 9 là Extension CL) — load
java.sql,java.xml... - App ClassLoader — load class của ứng dụng bạn viết
💡 💡 Cách nhớ
ClassLoader giống thủ thư: bạn cần quyển sách (class), thủ thư tra danh mục (tìm file .class), lấy sách về, kiểm tra còn nguyên vẹn không (verify), rồi đăng ký vào hệ thống (initialize).
5.2 Bytecode Verifier — bảo mật trước khi chạy
Trước khi execute, JVM chạy Bytecode Verifier để đảm bảo:
- Bytecode không vi phạm quy tắc kiểu (type safety)
- Không có stack overflow/underflow
- Không truy cập vùng nhớ ngoài phạm vi cho phép
- Không convert kiểu bất hợp pháp
Đây là lý do code Java không thể trực tiếp đọc vùng nhớ tùy ý như C — JVM bắt lỗi trước khi code chạy.
5.3 Execution Engine — interpret vs JIT
Execution Engine thực thi bytecode qua 2 con đường:
Interpreter (lần đầu):
- Đọc và thực thi từng bytecode instruction
- Không cần warm-up, chạy ngay
- Chậm (mỗi instruction phải decode + execute)
JIT Compiler (sau khi "warm up"):
- Phát hiện "hot methods" (gọi nhiều lần — ngưỡng mặc định ~10,000 lần)
- Compile toàn bộ method thành native machine code
- Cache native code → lần sau gọi thẳng machine code, không qua interpreter
- Có thể nhanh bằng C++ ở steady state
JIT không compile tất cả — chỉ compile hot code. Những method gọi hiếm vẫn interpret để tránh lãng phí compile time.
5.4 Runtime Data Area — bộ nhớ JVM
| Vùng | Mô tả | Scope |
|---|---|---|
| Heap | Chứa tất cả objects và arrays | Chia sẻ toàn JVM |
| JVM Stack | Stack frames cho method calls (local variables, operand stack, return address) | Mỗi thread một stack |
| Metaspace | Metadata của class (tên field, tên method, bytecode...) | Chia sẻ toàn JVM |
| PC Register | Program Counter — địa chỉ instruction đang thực thi | Mỗi thread một PC |
| Native Method Stack | Stack cho native methods (JNI) | Mỗi thread |
Khi bạn thấy lỗi OutOfMemoryError: Java heap space — Heap đầy. StackOverflowError — JVM Stack đầy (thường do recursion vô hạn).
5.5 Garbage Collector — dọn rác tự động
GC tự động giải phóng objects không còn được tham chiếu. Bạn không gọi free() — JVM lo.
Java 21 có nhiều GC algorithm, mỗi cái có trade-off khác nhau:
| GC | Đặc điểm | Dùng khi |
|---|---|---|
| G1 GC | Default từ Java 9. Throughput + latency cân bằng | Hầu hết app |
| ZGC | Pause time < 1ms, scalable đến TB heap | Latency-sensitive (trading, gaming) |
| Shenandoah | Concurrent, pause ngắn | App cần low latency, Red Hat maintained |
| Serial GC | Single-threaded, overhead thấp | Embedded, container nhỏ |
| Parallel GC | Throughput tối đa, pause dài hơn | Batch processing |
Chi tiết GC sẽ có ở module tuning. Bây giờ chỉ cần nhớ: GC tự động, không cần gọi tay.
6. Các JVM Distribution
"JVM" hay "JDK" không phải một sản phẩm duy nhất. Nhiều tổ chức build JDK từ cùng OpenJDK source, tạo ra JVM distributions khác nhau:
| Distribution | Nhà phát triển | Chi phí | Đặc điểm |
|---|---|---|---|
| Oracle JDK | Oracle | Miễn phí dev/test; trả phí production (từ Java 17) | Official, commercial support |
| Eclipse Temurin | Adoptium (Eclipse Foundation) | Miễn phí | Phổ biến nhất, khuyến nghị cho hầu hết |
| Azul Zulu | Azul Systems | Miễn phí (community); trả phí (premium) | LTS support dài, nhiều platform |
| Amazon Corretto | Amazon | Miễn phí | Optimize cho AWS, dùng trong Amazon production |
| GraalVM | Oracle + community | Community miễn phí; Enterprise trả phí | Native image (compile Java thành binary), polyglot |
| Microsoft OpenJDK | Microsoft | Miễn phí | Optimize cho Azure, Windows |
💡 💡 Chọn gì cho dự án mới?
Eclipse Temurin 21 LTS — miễn phí, phổ biến nhất, cộng đồng lớn, CI/CD integration tốt. Download tại adoptium.net. Nếu chạy trên AWS thì dùng Amazon Corretto không cần đổi gì — API tương đương Temurin.
Chi tiết về từng distribution, GraalVM native image, và jlink custom runtime sẽ có ở Module 13 — Môi trường và Tooling nâng cao.
7. Cài JDK — nhanh và đúng cách
7.1 Dùng SDKMAN (khuyến nghị — quản lý nhiều version)
# Cai SDKMAN
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
# Cai Temurin 21
sdk install java 21.0.3-tem
# Kiem tra
java -version
# openjdk version "21.0.3" 2024-04-16
# OpenJDK Runtime Environment Temurin-21.0.3+9 (build 21.0.3+9)
7.2 Kiểm tra cài đặt
# Version Java
java -version
# Version compiler
javac -version
# Xem JAVA_HOME
java -XshowSettings:property -version 2>&1 | grep java.home
7.3 Hiểu JAVA_HOME
JAVA_HOME là biến môi trường trỏ tới thư mục cài JDK. Nhiều tool (Maven, Gradle, IDE) đọc JAVA_HOME để tìm JDK.
# macOS / Linux -- them vao ~/.zshrc hoac ~/.bashrc
export JAVA_HOME=$(/usr/libexec/java_home -v 21) # macOS
export PATH=$JAVA_HOME/bin:$PATH
# Kiem tra
echo $JAVA_HOME
# /Users/you/.sdkman/candidates/java/21.0.3-tem
8. Verify — chạy Hello World để xác nhận JDK hoạt động
// HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, JVM!");
System.out.println("Java version: " + System.getProperty("java.version"));
System.out.println("JVM vendor: " + System.getProperty("java.vm.vendor"));
System.out.println("JVM name: " + System.getProperty("java.vm.name"));
}
}
# Compile
javac HelloWorld.java
# Chay
java HelloWorld
Output mẫu:
Hello, JVM!
Java version: 21.0.3
JVM vendor: Eclipse Adoptium
JVM name: OpenJDK 64-Bit Server VM
Nếu thấy output này — JDK đã hoạt động. Bài 3 sẽ đi vào cấu trúc chương trình Java đầu tiên.
9. Pitfall thực tế
❌ Nhầm 1: Cài JRE standalone rồi không javac được.
✅ Cài JDK (bao gồm JRE + compiler). Từ Java 9+, JRE standalone không còn được release.
❌ Nhầm 2: Máy có nhiều JDK, java -version ra version cũ.
✅ Kiểm tra which java và echo $JAVA_HOME. Dùng SDKMAN sdk use java 21.0.3-tem để switch.
❌ Nhầm 3: Tưởng JVM là một — thực ra mỗi process Java chạy JVM instance riêng. ✅ Mở 3 tab terminal chạy 3 app Java → 3 JVM processes riêng biệt, heap riêng, GC riêng.
❌ Nhầm 4: Dùng Oracle JDK cho production mà không có license. ✅ Dùng Eclipse Temurin hoặc Amazon Corretto — miễn phí hoàn toàn, API tương đương.
❌ Nhầm 5: Trong Dockerfile dùng JDK full cho container chạy app — image nặng ~400MB.
✅ Dùng eclipse-temurin:21-jre-alpine (~90MB) hoặc jlink để tạo minimal runtime.
10. 📚 Deep Dive Oracle
ℹ️ 📚 Deep Dive Oracle (optional)
Spec chính thức:
- JVM Specification SE21 — full spec kiến trúc JVM: ClassLoader, bytecode, memory areas, execution engine
- JDK 21 Tool Docs — tài liệu tất cả command-line tools: javac, java, jar, javap, jdb, jlink...
- JDK 21 API Docs — Javadoc toàn bộ Java Class Library
- JEP 439 — Generational ZGC — ZGC trong Java 21 (finalized)
- JEP 444 — Virtual Threads — Project Loom, virtual threads finalized trong Java 21
Ghi chú đọc spec: JVM Spec Chapter 2 "The Structure of the Java Virtual Machine" mô tả chi tiết Runtime Data Areas (heap, stack, metaspace...). Chapter 5 "Loading, Linking, and Initializing" giải thích ClassLoader delegation model. Đây là nền tảng để hiểu ClassLoader issues và PermGen/Metaspace OOM errors.
11. Tóm tắt
- JVM = bytecode execution engine. Chạy file
.class, có ClassLoader + Verifier + JIT + GC + Runtime Data Areas. - JRE = JVM + Java Class Library. Đủ để chạy app Java.
- JDK = JRE + development tools (
javac,jar,jdb...). Cần để viết và build app. - JDK ⊃ JRE ⊃ JVM — developer luôn cài JDK.
- JVM có nhiều vùng nhớ: Heap (objects), JVM Stack (method frames), Metaspace (class metadata).
- JIT Compiler tăng tốc bằng cách compile hot methods thành native code.
- GC tự động dọn objects không còn reference. Java 21 default là G1 GC.
- Nhiều JVM distribution: Temurin (phổ biến nhất), Corretto (AWS), Oracle JDK (trả phí production), GraalVM (native image).
12. Tự kiểm tra
- Tại sao JVM phải là implementation khác nhau trên mỗi OS, trong khi bytecode lại portable?
- ByteCode Verifier làm gì trước khi JVM chạy code? Điều này bảo vệ bạn khỏi gì?
- JIT Compiler khác Interpreter ở điểm nào? Tại sao JVM không JIT compile tất cả mọi method?
- Khi nào một object trên Heap bị GC dọn? "Không còn reference" nghĩa là gì cụ thể?
- Bạn deploy app Java lên Docker. Nên dùng image base nào —
eclipse-temurin:21-jdkhayeclipse-temurin:21-jre? Vì sao?
Bài tiếp theo: Cài đặt môi trường và Hello World đầu tiên