Danh mục:
Bài viết này tóm tắt các kiến thức cốt lõi về cấu trúc bộ nhớ JVM và cơ chế hoạt động của Garbage Collection (GC).
1. Hiểu về cấu trúc bộ nhớ JVM
JVM sử dụng các vùng bộ nhớ chính sau trong quá trình thực thi:
- Metaspace: Lưu trữ thông tin meta của lớp (class structure, method info...).
- Heap: Lưu trữ các thực thể đối tượng (object instances). Hầu hết các đối tượng new được tạo ra ở đây.
- Stack: Tồn tại theo từng luồng (thread), lưu trữ các biến cục bộ (local variables) và ngữ cảnh gọi hàm.
- Code Cache: Lưu trữ kết quả của trình biên dịch JIT.
2. Các bước xử lý cơ bản của GC (Garbage Collection)
- Tạo đối tượng: Đối tượng mới được phân bổ vào vùng Eden.
- Mark (Đánh dấu): Truy vết các đối tượng có thể tiếp cận được từ GC Root.
- Sweep/Copy/Compact:
- Young GC: Sao chép các đối tượng còn sống sang vùng Survivor hoặc Old.
- Old GC: Sử dụng Mark-Compact để giải quyết vấn đề phân mảnh bộ nhớ.
- Promotion (Thăng cấp): Các đối tượng sống sót qua một số lần GC nhất định sẽ được đưa lên vùng Old.
3. Tóm tắt cấu trúc G1 GC (GC mặc định từ JDK 17)
Chia toàn bộ Heap thành các vùng có kích thước cố định gọi là Region (Eden, Survivor, Old, Humongous).
Các loại GC:
- Young GC: Quét vùng Eden -> Survivor.
- Mixed GC: Thu gom cả vùng Young và một phần vùng Old.
- Full GC: Thu gom toàn bộ Heap (nên cố gắng tránh trường hợp này).
4. Mẹo quản lý Metaspace
- Metaspace nằm ở bộ nhớ gốc (Native Memory) chứ không nằm trong Heap.
- Mặc định là không giới hạn dung lượng -> có thể giới hạn bằng -XX:MaxMetaspaceSize.
- Rủi ro rò rỉ Metaspace tăng cao khi sử dụng ClassLoader động hoặc SPI.
5. Chiến lược quản lý đối tượng có lợi cho GC
- Giải phóng tham chiếu: Giải phóng tường minh bằng cách gán obj = null.
- Thu hẹp phạm vi Sử dụng biến cục bộ trong phương thức hoặc khối lệnh (block).
- Tận dụng Escape Analysis Nếu đối tượng không lộ ra ngoài, JVM sẽ phân bổ trên Stack.
- Sử dụng WeakReference Rất hữu ích khi làm bộ nhớ đệm (cache).
- Quản lý ThreadLocal Bắt buộc gọi .remove() sau khi sử dụng để tránh rò rỉ bộ nhớ trong ThreadPool.
Lưu ý: Lambda Capture và ThreadLocal
- Lambda Capture: Các đối tượng bị "capture" trong lambda sẽ được lưu trữ dưới dạng các trường (fields) bên trong. Nếu tham chiếu vẫn còn (ví dụ trong Executor), đối tượng sẽ không được GC thu hồi.
- ThreadLocal: Tham chiếu sẽ được duy trì cho đến khi Thread kết thúc. Cần đặc biệt lưu ý khi sử dụng với ThreadPool.
Java try { threadLocal.set(value); // ... xử lý logic } finally { threadLocal.remove(); // Luôn luôn dọn dẹp trong khối finally }
Công cụ & Câu lệnh đề xuất
-
jcmd <pid> VM.native_memory summary: Xem tóm tắt bộ nhớ gốc. -
-XX:+PrintGCDetails -Xlog:gc*: Xem log chi tiết của GC. - Công cụ phân tích: GCViewer, GCEasy.io.
💡 Hiểu rõ cấu trúc bộ nhớ và cơ chế GC của JVM giúp bạn ngăn ngừa các sự cố bộ nhớ ngoài ý muốn, đồng thời nâng cao hiệu năng và sự ổn định cho hệ thống.
Nguồn bài viết ryukato.github.io