Java Foundations/JVM, JRE, JDK — ba khái niệm nền tảng
3/39
Bài 3 / 39~12 phútNhập môn & Tư duy lập trìnhMiễn phí lượt xem

JVM, JRE, JDK — ba khái niệm nền tảng

Phân biệt JVM, JRE, JDK một lần cho rõ: cái gì chạy code, cái gì đủ để deploy, cái gì cần để viết code. Hiểu kiến trúc bên trong JVM.

TL;DR: JVM (Java Virtual Machine) là máy ảo đọc và chạy bytecode file .class — nó là động cơ thuần. JRE (Java Runtime Environment) = JVM cộng thư viện chuẩn Java, đủ để chạy một app đã build sẵn. JDK (Java Development Kit) = JRE cộng công cụ biên dịch như javacjar, cần để viết và build code. Quan hệ là lồng nhau: JDK chứa JRE chứa JVM. Quy tắc chọn: developer luôn cài JDK; production server hoặc Docker image có thể dùng JRE để nhẹ hơn; máy chỉ chạy app desktop dùng JRE là đủ.

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 đượcnâng cấp được xe.

Ánh xạ sang Java:

Xe hơiJava
Độ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).

Bài 01 đã giới thiệu bytecode cùng JVM qua analogy; ở đây ta phân biệt rõ JVM, JRE và JDK.

JVM làm ba việc chính:

  1. Load file .class vào bộ nhớ
  2. Verify bytecode (kiểm tra không có lệnh nguy hiểm)
  3. 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, streams
  • java.net — HTTP, socket, URL
  • java.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:

ToolLệnhDùng để
CompilerjavacCompile .java sang .class
LauncherjavaChạy app từ .class hoặc .jar
ArchiverjarĐóng gói thành .jar file
DisassemblerjavapXem bytecode của .class
DebuggerjdbDebug chương trình đang chạy
DocumentationjavadocGenerate HTML docs từ Javadoc comments
ProfilerjconsoleMonitor JVM heap, threads, GC — VisualVM mạnh hơn nhưng là download riêng (tách khỏi JDK từ Java 9)
Key toolkeytoolQuả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 JavaJDKCầ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 appJDK hoặc JREJDK để có thêm monitoring tools; JRE đủ nếu chỉ run
CI/CD pipeline build + testJDKCần compile và chạy test
Docker container chạy .jarJRE 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 — nhìn tổng quan

Hiểu sơ kiến trúc JVM giúp bạn đọc được stack trace và hình dung khi nào lỗi OutOfMemoryError xuất hiện. Đây chỉ là cái nhìn tổng quan ở mức nhập môn — chi tiết từng phần thuộc course nâng cao.

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)"]
      stack["JVM Stacks<br/>(frames per thread)"]
    end
    gc["Garbage Collector"]
  end

  src --> cl --> bv --> ee
  ee --> rda
  gc -.-> heap

Mỗi component chính làm một việc:

  • ClassLoader — đọc file .class từ disk, đưa vào bộ nhớ rồi khởi tạo (load, link, initialize) trước khi class được dùng.
  • Bytecode Verifier — kiểm tra bytecode hợp lệ và an toàn kiểu trước khi chạy; đây là lý do code Java không thể đọc vùng nhớ tùy ý như C.
  • Execution Engine — chạy bytecode bằng Interpreter ở lần đầu (chạy ngay nhưng chậm), rồi JIT Compiler biên dịch những method nóng (gọi nhiều) thành native machine code để chạy nhanh hơn ở steady state.
  • Vùng nhớHeap chứa mọi object (đầy thì gặp OutOfMemoryError: Java heap space); JVM Stack chứa stack frame của mỗi lần gọi method, mỗi thread một stack (tràn thì gặp StackOverflowError, thường do đệ quy vô hạn). Garbage Collector tự động dọn object không còn tham chiếu — bạn không gọi free().
⏭️ Học sâu hơn ở đâu

Chi tiết Garbage Collector (G1, ZGC, Shenandoah...) và đầy đủ Runtime Data Area (Metaspace, PC Register, Native Method Stack...) sẽ học ở course Java Internals (tier 3). Ở nhập môn, bạn chỉ cần nắm bức tranh tổng quan trên.

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:

DistributionNhà phát triểnChi phíĐặc điểm
Oracle JDKOracleMiễn phí dev/test; trả phí production (từ Java 17)Official, commercial support
Eclipse TemurinAdoptium (Eclipse Foundation)Miễn phíPhổ biến nhất, khuyến nghị cho hầu hết
Azul ZuluAzul SystemsMiễn phí (community); trả phí (premium)LTS support dài, nhiều platform
Amazon CorrettoAmazonMiễn phíOptimize cho AWS, dùng trong Amazon production
GraalVMOracle + communityCommunity miễn phí; Enterprise trả phíNative image (compile Java thành binary), polyglot
Microsoft OpenJDKMicrosoftMiễ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.

Việc cài JDK (qua SDKMAN/Homebrew, cấu hình JAVA_HOME) và bước verify bằng chương trình Hello World đầu tiên được hướng dẫn từng bước ở hai bài tiếp theo: Cài đặt môi trườngHello World.

7. 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 javaecho $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.

8. 📚 Deep Dive Oracle

📚 Deep Dive Oracle (optional)

Spec chính thức:

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.

9. Liên hệ các bài khác

  • Java là gì — bài này dựa trên khái niệm bytecode và JVM đã giới thiệu ở đó; đọc lại nếu bạn còn mơ hồ vì sao Java cần một "máy ảo" để chạy.
  • Cài đặt môi trường — áp dụng ngay quyết định "chọn JDK nào": bài đó hướng dẫn cài JDK (Temurin) qua SDKMAN/Homebrew và cấu hình JAVA_HOME.
  • Compile và Run — đi sâu vào hai công cụ javacjava của JDK mà bài này mới chỉ giới thiệu tên: vòng đời từ .java sang .class rồi chạy trên JVM.

10. Tóm tắt

  • JVM = bytecode execution engine. Chạy file .class, gồm ClassLoader + Verifier + Execution Engine (Interpreter + JIT) + GC + vùng nhớ.
  • 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ó hai vùng nhớ chính cần nhớ ở nhập môn: Heap (chứa object) và JVM Stack (chứa frame mỗi method call).
  • JIT Compiler tăng tốc bằng cách compile hot methods thành native code; GC tự động dọn object không còn reference.
  • Nhiều JVM distribution: Temurin (phổ biến nhất), Corretto (AWS), Oracle JDK (trả phí production), GraalVM (native image).

11. Tự kiểm tra

Tự kiểm tra
Q1
Tại sao JVM phải là implementation khác nhau trên mỗi OS, trong khi bytecode lại portable?
Bytecode là định dạng lý tưởng — mô tả "phải làm gì" chứ không mô tả "làm thế nào trên phần cứng này". Nhưng app thật cần đọc file, cấp phát bộ nhớ, tạo thread — những thao tác này phải gọi system call của OS, mà Windows/Linux/macOS có API native khác nhau. JVM của mỗi OS là lớp dịch: nhận bytecode chung, gọi API native tương ứng. Hệ quả: bytecode portable vì JVM "gánh" phần OS-specific.
Q2
ByteCode Verifier làm gì trước khi JVM chạy code? Điều này bảo vệ bạn khỏi gì?
Verifier kiểm tra file .class hợp lệ trước khi thực thi: operand stack không underflow/overflow, type của tham số khớp với opcode, không truy cập ngoài mảng, không gọi private method của class khác, final class không bị subclass... Bảo vệ JVM khỏi crash do bytecode hỏng (bị sửa tay hoặc sinh sai), và chặn bytecode độc cố tình vi phạm type safety — điều tối quan trọng khi JVM chạy code đến từ nguồn không tin cậy (applet cũ, plugin, downloaded class).
Q3
JIT Compiler khác Interpreter ở điểm nào? Tại sao JVM không JIT compile tất cả mọi method?
JVM ban đầu interpret mọi method — đọc từng bytecode instruction và thực thi ngay, khởi động nhanh nhưng không tối ưu. Method nào được gọi nhiều lần (hot) thì JIT mới compile sang machine code kèm optimization (inline, escape analysis, loop unrolling) rồi cache lại — vì compile tốn CPU/RAM, không đáng làm cho method chỉ chạy 1–2 lần. JVM phát hiện method hot sau hàng nghìn lần gọi; đây là lý do Java "chậm lúc khởi động, nhanh dần khi chạy lâu" (warm-up). Chi tiết ngưỡng cụ thể (tiered compilation, Tier 4…) thuộc phần JVM internals — sẽ đào sâu ở khoá Java Internals.
Q4
Đồng nghiệp chỉ cài JRE rồi gõ `javac App.java` để build, máy báo "command not found". Thiếu cái gì? Để vừa viết vừa chạy code thì nên cài JVM, JRE hay JDK?
JRE chỉ gồm JVM cộng thư viện chuẩn — đủ để chạy app đã build sẵn, nhưng không có javac (trình biên dịch) nên không build được, vì thế lệnh báo not found. Để vừa viết vừa chạy phải cài JDK: JDK bao trùm JRE và bổ sung công cụ phát triển (javac, jar, javap, jdb). JVM không phải thứ cài riêng lẻ — nó nằm bên trong cả JRE lẫn JDK. Quy tắc: viết code → JDK; chỉ deploy để chạy → JRE đủ.
Q5
Bạn deploy app Java lên Docker. Nên dùng image base nào — eclipse-temurin:21-jdk hay eclipse-temurin:21-jre? Vì sao?
Dùng eclipse-temurin:21-jre cho production. JRE nhỏ hơn JDK đáng kể — với image eclipse-temurin, bản JRE thường nhẹ hơn bản JDK khoảng 130-180MB (không có javac, javap, jdb), giảm attack surface (compiler không có mặt trong prod = kẻ tấn công không dùng nó được), giảm pull/deploy time. Chỉ cần JDK khi app phải compile code runtime (JSP, Groovy script, dynamic class generation). Tối ưu thêm: eclipse-temurin:21-jre-alpine (~90MB) hoặc jlink custom runtime.

Bài tiếp theo: Cài đặt môi trường và Hello World đầu tiên

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên