Công cụ Phân tích Bộ nhớ JVM: jmap

jmap (Java Memory Map) là công cụ chẩn đoán quan trọng của JDK, được sử dụng để **xem xét tình trạng sử dụng bộ nhớ của tiến trình Java, tạo bản ghi đống (Heap Dump), và phân tích phân bố đối tượng**. Công cụ này dựa vào cơ chế Attach của JVM và thường được dùng để xác định rò rỉ bộ nhớ hoặc các vấn đề OOM.

1. Ngữ pháp cơ bản

jmap [options] <pid>
jmap [options] <executable <core>
jmap [options] [server_id@]<remote server IP or hostname>

️ **Trong môi trường sản xuất, cách sử dụng phổ biến nhất là dạng đầu tiên: `jmap [options] <pid>`**

2. Giải thích chi tiết các tùy chọn chính (theo mục đích)

A. Xem tổng quan về bộ nhớ đống (An toàn, không STW)

`jmap -heap <pid>`

  • Hiển thị cấu hình và trạng thái sử dụng hiện tại của đống JVM.
  • **Không kích hoạt GC, không dừng (An toàn)**.
  • Nội dung đầu ra bao gồm:
    • Thuật toán GC (ví dụ G1, Parallel, CMS).
    • Kích thước đống (Xmx/Xms).
    • Dung lượng và mức sử dụng của từng thế hệ (Eden, Survivor, Old).
    • Sử dụng Metaspace (JDK 8+).

Ví dụ đoạn đầu ra:

using thread-local object allocation.
Garbage Collector(s) detected: PS Scavenge, PS MarkSweep
Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 1431306240 (1365.0MB)
   MaxNewSize               = 1431306240 (1365.0MB)
Heap Usage:
PS Young Generation
Eden Space:
   capacity = 1398235136 (1333.5MB)
   used     = 1207959552 (1152.0MB)
...

**Mục đích**: Nhanh chóng xác nhận xem đống có sắp đầy hay không, thuật toán GC có phù hợp với kỳ vọng hay không.

B. Histogram đối tượng (Có thể chọn có kích hoạt Full GC hay không)

`jmap -histo[:live] <pid>`

  • Thống kê số lượng thực thể và bộ nhớ chiếm dụng bởi các đối tượng trong đống.
  • **`:live` là tùy chọn then chốt!**
Tùy chọnHành độngCó kích hoạt Full GC?Mục đích
-histoThống kê tất cả các đối tượng (bao gồm những đối tượng chờ thu dọn)KhôngXem nhanh (nhưng có thể chứa rác)
-histo:liveChỉ thống kê các đối tượng **có thể truy cập được** (đối tượng sống)**Phân tích chính xác rò rỉ bộ nhớ**

Định dạng đầu ra:

 num     #instances         #bytes  class name
----------------------------------------------
   1:        100000       16000000  java.util.HashMap$Node
   2:         50000        4000000  com.example.CacheEntry
   3:         10000        1200000  byte[]

️ **Cảnh báo**: `-histo:live` sẽ buộc thực thi **Full GC** (Stop-The-World), cần cẩn thận khi sử dụng trong môi trường sản xuất!

Gợi ý sử dụng (lấy 20 dòng đầu):

jmap -histo:live 12345 | head -20

C. Tạo tệp ghi đống (Heap Dump)

`jmap -dump:[live,]format=b,file=<filename> <pid>`

  • Tạo bản ghi đống ở định dạng HPROF tiêu chuẩn, để phân tích bằng MAT, VisualVM, v.v.
  • **`live` quyết định chỉ ghi lại các đối tượng tồn tại**.
Tùy chọnGiải thích
liveChỉ ghi lại **các đối tượng có thể truy cập được** (khuyến nghị, tệp nhỏ hơn, phân tích chính xác hơn)
format=bĐịnh dạng nhị phân (định dạng duy nhất hỗ trợ)
file=xxx.hprofĐường dẫn tệp đầu ra

Lệnh khuyến nghị:

jmap -dump:live,format=b,file=/tmp/heap_$(date +%s).hprof 12345

️ **Tác động quan trọng**:

  • Sẽ kích hoạt **Full GC** (vì phải đánh dấu các đối tượng tồn tại).
  • Ứng dụng sẽ **hoàn toàn tạm dừng (STW)**, thời gian kéo dài = kích thước đống / tốc độ quét.
  • 1GB đống ≈ vài giây đến vài chục giây tạm dừng, đống lớn (>10GB) có thể mất hàng phút!

**Khuyến nghị trong sản xuất**:

  • Không sử dụng trừ khi cần thiết.
  • Đảm bảo có đủ không gian ổ đĩa (tệp HPROF ≈ kích thước các đối tượng tồn tại trong đống).
  • Xem xét sử dụng `-XX:+HeapDumpOnOutOfMemoryError` để tự động ghi lại.

D. Xem ánh xạ bộ nhớ (các đoạn bộ nhớ cấp thấp)

`jmap -permstat <pid>` (JDK 8 và thấp hơn)

  • Hiển thị thông tin thống kê về các tải lớp trong PermGen.
  • **Bị loại bỏ từ JDK 8+** (Metaspace thay thế PermGen).

`jmap -finalizerinfo <pid>`

  • Hiển thị số lượng đối tượng đang chờ finalize trong hàng đợi finalization.
  • Có thể dùng để xác định các vấn đề bộ nhớ do phương thức `finalize()` gây ra.

️ Một số tùy chọn đã bị loại bỏ trong JDK 11+.

3. Các tùy chọn bị loại bỏ hoặc không khuyến nghị

Tùy chọnTrạng tháiGiải thích
-FHỗ trợ hạn chếChế độ ép buộc (giống như jstack -F), sử dụng ptrace, thường thất bại trong container.
-d64Bị loại bỏJVM 64 bit đã bật mặc định.
-clstatsBị loại bỏThống kê tải lớp, chức năng bị `-histo` và JMX thay thế.

**Không nên sử dụng `jmap -F`**: Trong container, Alpine, và các môi trường an ninh tăng cường gần như luôn thất bại.

4. Vấn đề phổ biến và hạn chế

1. Lỗi "Unable to open socket file"

  • Nguyên nhân: Cơ chế Attach thất bại (/tmp không thể viết, quyền người dùng, ảnh Alpine, v.v.).
  • Giải pháp:
    • Sử dụng `jcmd <pid> GC.run_finalization` (xem tổng quan đống).
    • Sử dụng `kill -3 <pid>` để lấy ngăn xếp luồng (nhưng không thay thế được heap dump).
    • Đảm bảo `/tmp` trong container có thể viết.

2. Tạo tệp dump quá chậm/hang

  • Nguyên nhân: Đống quá lớn, hoặc IO đĩa chậm.
  • Gợi ý:
    • Dùng `jmap -histo:live` để nhanh chóng xác định các đối tượng lớn.
    • Xem xét sử dụng **Async-Profiler + JFR** (JDK 11+) làm mẫu chi phí thấp.

3. Không thể phân tích tệp dump?

  • Đảm bảo sử dụng cùng hoặc phiên bản JDK cao hơn để phân tích.
  • Công cụ khuyến nghị:
    • **Eclipse MAT** (Memory Analyzer): Miễn phí, mạnh mẽ.
    • **VisualVM**: Phân tích cơ bản tích hợp sẵn.
    • **JProfiler / YourKit**: Thương mại, trải nghiệm tương tác tốt.

5. Khuyến nghị thực hành tốt nhất

Trường hợpLệnh khuyến nghịLưu ý
Xem nhanh việc sử dụng đống`jmap -heap <pid>`An toàn, không dừng
Nghi ngờ rò rỉ bộ nhớ`jmap -histo:live <pid> | head -30`Sẽ Full GC, tránh giờ cao điểm
Cần phân tích sâu`jmap -dump:live,format=b,file=heap.hprof <pid>`Thông báo trước, đảm bảo không gian đĩa
Attach trong container thất bạiSử dụng `jcmd <pid> VM.flags` + nhật ký ứng dụngHoặc thêm `-XX:+HeapDumpOnOutOfMemoryError` khi khởi động

6. So sánh với các công cụ khác

Chức năng`jmap``jcmd`Giải thích
Tổng quan đống`jmap -heap``jcmd <pid> GC.run_finalization``jcmd` hiện đại hơn
Histogram đối tượng`jmap -histo`Không hỗ trợ`jmap` vẫn là lựa chọn duy nhất
Heap Dump`jmap -dump``jcmd <pid> GC.run_finalization` (không thể dump)`jmap` không thể thay thế
Ngăn xếp luồngKhông hỗ trợ`jcmd <pid> Thread.print`Sử dụng `jcmd` hoặc `jstack`

**Kết luận**: `jmap` vẫn là công cụ cốt lõi không thể thay thế trong **thống kê đối tượng** và **gắn kết đống**.

Thẻ: JVM jmap Heap Dump Memory Analysis

Đăng vào ngày 14 tháng 6 lúc 08:03