Garbage Collection & Production Diagnostics
Mục tiêu không phải thuộc flag JVM, mà hiểu memory pressure, collector trade-off và quy trình thu thập bằng chứng trước khi sửa.
1. Memory của JVM process
| Vùng | Nội dung |
|---|---|
| Java heap | Object/array do application tạo, được GC quản lý. |
| Thread stack | Frame, local variable, operand stack; mỗi thread có stack riêng. |
| Metaspace | Class metadata ở native memory; class loader leak làm metaspace tăng. |
| Code cache | Machine code do JIT compile. |
| Direct/native memory | DirectByteBuffer, JNI, GC structures, JVM internal allocation. |
-Xmx chỉ giới hạn heap, không giới hạn toàn process. Container memory limit phải chừa headroom cho non-heap/native. Nhiều platform thread làm thread stack đáng kể; virtual thread stack được quản lý khác và co giãn.
2. Reachability và reference
GC bắt đầu từ roots như active thread stack, static field, JNI reference rồi đánh dấu object reachable. Strong reference giữ object sống. Soft/weak/phantom reference có semantics khác và không nên dùng như cache policy tùy tiện. Cleaner/phantom phù hợp fallback cleanup native resource, nhưng deterministic resource vẫn cần close.
finalize().3. Các khái niệm collector
Collector có thể dùng mark, sweep, compact, copying và region. Generational hypothesis nói phần lớn object chết trẻ, nên nhiều collector tối ưu young collection. Stop-the-world nghĩa application thread dừng ở safepoint; concurrent phase vẫn có thể dùng CPU/bandwidth cùng ứng dụng.
| Collector Java 21 | Định hướng |
|---|---|
| Serial GC | Đơn giản, một GC thread; heap nhỏ hoặc environment hạn chế. |
| Parallel GC | Ưu tiên throughput, dùng nhiều GC thread, pause thường dài hơn. |
| G1 GC | Mặc định phổ biến; region-based, cân bằng throughput và pause target. |
| ZGC | Low-latency, phần lớn công việc concurrent; Java 21 có generational ZGC là lựa chọn cần bật. |
| Shenandoah | Low-pause collector trong các OpenJDK distribution hỗ trợ; không phải mọi distribution giống nhau. |
4. G1 mental model
Heap chia region. Young collection thu Eden/Survivor; concurrent marking tìm live object toàn heap; mixed collection thu young cùng một số old regions có nhiều garbage. Humongous object chiếm một hoặc nhiều region riêng và có thể gây pressure/fragmentation. Pause goal là mục tiêu heuristic, không phải SLA bảo đảm.
5. Đọc GC log
java -Xlog:gc*,safepoint:file=gc.log:time,uptime,level,tags ...
Cần quan sát: heap before/after, pause time, cause, frequency, allocation rate, promotion, concurrent cycle và failure. GC chạy thường xuyên nhưng reclaim tốt có thể do heap nhỏ hoặc allocation cao; old occupancy chỉ tăng và không giảm gợi ý retention/leak; long pause có thể do live set lớn, humongous allocation, reference processing hoặc safepoint khác GC.
6. Công cụ thu thập bằng chứng
jcmd <pid> VM.version
jcmd <pid> VM.flags
jcmd <pid> GC.heap_info
jcmd <pid> GC.class_histogram
jcmd <pid> Thread.print -l
jcmd <pid> VM.native_memory summary
jcmd <pid> JFR.start name=incident settings=profile duration=120s filename=incident.jfr
JFR ghi event CPU, allocation, lock, file/socket I/O, GC và thread với overhead thấp phù hợp production. Thread dump là snapshot; lấy chuỗi snapshot. Class histogram cho count/bytes theo class nhưng không cho biết ai giữ object; heap dump + dominator/retained size mới giúp tìm retention path. Heap dump có thể lớn, gây pause và chứa dữ liệu nhạy cảm.
Native Memory Tracking cần bật từ startup và có overhead; dùng baseline/diff để thấy category tăng. OS tools vẫn cần để map process/thread CPU, RSS, page fault và file descriptor.
7. Quy trình theo triệu chứng
CPU cao
- Xác nhận process và thời gian xảy ra.
- Map native thread ID CPU cao sang Java thread dump.
- Lấy nhiều dump/JFR để phân biệt loop, contention, GC hay workload hợp lệ.
- Đo trước khi tối ưu.
OutOfMemoryError
| Thông báo | Hướng điều tra |
|---|---|
| Java heap space | Heap sizing, allocation burst, retention path, unbounded data. |
| GC overhead limit exceeded | Gần hết heap, GC tốn phần lớn thời gian nhưng reclaim rất ít. |
| Metaspace | Số class/loader, dynamic generation, redeploy leak. |
| Unable to create native thread | Thread count, OS limit, native memory; không nhất thiết heap. |
| Direct buffer memory | Direct buffer allocation/lifetime/limit. |
Latency cao
Đặt latency trên timeline cùng GC pause, safepoint, CPU, lock, pool/queue và downstream. Correlation không tự là causation. P99 tăng trong khi average ổn thường cần phân tích tail và saturation.
8. JIT và warmup
Hot methods được tiered compilation tối ưu dần; deoptimization có thể xảy ra khi assumption không còn đúng. Benchmark ngắn dễ đo interpreter/compilation thay vì steady state. Dùng JMH để có warmup, fork, dead-code prevention và thống kê phù hợp. Production startup, warmup và peak throughput là ba mục tiêu khác nhau.
9. Nguyên tắc tuning
- Xác định SLO: throughput, latency percentile, footprint hay startup.
- Thu thập baseline và workload đại diện.
- Đổi một nhóm biến có giả thuyết rõ, so sánh bằng số liệu.
- Ưu tiên sửa retention/allocation/blocking trong code trước flag tinh chỉnh.
- Ghi lại JDK build, container limit, flags và traffic vì kết quả phụ thuộc môi trường.