jcmd: Công cụ chẩn đoán đa năng cho ứng dụng Java hiện đại

Giới thiệu về jcmd

jcmd là công cụ dòng lệnh được giới thiệu từ JDK 7, đóng vai trò như một giải pháp thống nhất để thay thế các tiện ích truyền thống như jstack, jmap, và jinfo. Dựa trên cơ chế Attach API của JVM, jcmd cho phép tương tác trực tiếp với tiến trình Java đang chạy nhằm thu thập thông tin trạng thái, thực thi hành động điều khiển hoặc truy xuất cấu hình — biến nó thành lựa chọn hàng đầu khi cần chẩn đoán sự cố trong môi trường sản phẩm.

Cú pháp cơ bản

jcmd <pid | tên_class_chính> <lệnh> [tùy_chọn]
  • pid: ID tiến trình Java (ưu tiên sử dụng).
  • tên_class_chính: Tên lớp chứa phương thức main(), chỉ dùng khi không xác định được PID.
  • lệnh: Hành động chẩn đoán mong muốn, ví dụ: Thread.print, GC.run.
  • tùy_chọn: Tham số bổ sung tùy theo lệnh.

Để xem danh sách đầy đủ các lệnh hỗ trợ:

jcmd <pid> help

Nhóm lệnh theo chức năng chính

1. Phân tích luồng và ngăn xếp

Thread.print – Ghi lại hình ảnh ngăn xếp tất cả luồng (thế chỗ jstack):

jcmd 12345 Thread.print -l > stacktrace.log
  • Tùy chọn -l hiển thị chi tiết khóa (lock), bao gồm cả các đồng bộ hóa từ java.util.concurrent.
  • An toàn để dùng trong hệ thống đang vận hành vì không gây Full GC.

Từ JDK 17 trở đi, có thêm lệnh:

jcmd 12345 Thread.dump_to_file /var/log/app/thread-dump.txt

Ghi trực tiếp vào file, tránh rủi ro do shell xử lý sai luồng đầu ra.

2. Quản lý bộ nhớ và thu gom rác

GC.run – Kích hoạt thu gom rác toàn phần:

jcmd 12345 GC.run

Lưu ý quan trọng: Gây dừng ứng dụng tạm thời (STW). Chỉ nên dùng để kiểm thử hoặc giải phóng bộ nhớ khẩn cấp.

GC.run_finalization – Thực thi hàng đợi finalize:

jcmd 12345 GC.run_finalization

Kích hoạt phương thức finalize() của các đối tượng chờ hủy. Không khuyến khích phụ thuộc vào tính năng này.

VM.gc_class_histogram – Hiển thị biểu đồ số lượng đối tượng đang sống:

jcmd 12345 VM.gc_class_histogram

Tương đương jmap -histo:live, nhưng sẽ kích hoạt Full GC. Nên cân nhắc kỹ trước khi dùng.

Nếu muốn thống kê lớp mà không gây Full GC, có thể dùng:

jcmd 12345 GC.class_stats

Yêu cầu khởi động JVM kèm cờ -XX:+UnlockDiagnosticVMOptions.

3. Kiểm tra cấu hình và thông tin hệ thống

VM.flags – Xem các tham số khởi động JVM:

jcmd 12345 VM.flags
jcmd 12345 VM.flags -all  # Bao gồm cả giá trị mặc định

Dễ dàng tìm kiếm cài đặt cụ thể như heap size:

jcmd 12345 VM.flags | grep MaxHeap

VM.system_properties – Liệt kê tất cả thuộc tính hệ thống (-Dkey=value):

jcmd 12345 VM.system_properties

Bao gồm user.dir, file.encoding, spring.profiles.active

VM.command_line (JDK 9+) – Hiển thị câu lệnh đầy đủ dùng để khởi động ứng dụng:

jcmd 12345 VM.command_line

VM.version – In phiên bản chi tiết của JVM:

jcmd 12345 VM.version

4. Phân tích bộ nhớ native (cần bật NMT)

Phải khởi động JVM với:

-XX:NativeMemoryTracking=summary

hoặc

-XX:NativeMemoryTracking=detail

Sau đó dùng lệnh:

jcmd 12345 VM.native_memory summary

Xem tổng hợp bộ nhớ theo nhóm: Class, Thread, Code Cache, GC, Internal…

Một số tùy chọn nâng cao:

Tùy chọn Mô tả
summary Tổng quan theo loại bộ nhớ
detail Chi tiết từng khối bộ nhớ (khá dài)
baseline Thiết lập mốc so sánh ban đầu
summary.diff Hiển thị sự thay đổi kể từ mốc baseline

Ví dụ theo dõi rò rỉ bộ nhớ native:

jcmd 12345 VM.native_memory baseline
sleep 60
jcmd 12345 VM.native_memory summary.diff

Phần Internal thường liên quan đến DirectByteBuffer, Netty, JNI.

5. Giám sát hiệu năng

PerfCounter.print – In các bộ đếm nội bộ của JVM:

jcmd 12345 PerfCounter.print

Hiển thị số lần GC, thời gian biên dịch Just-In-Time, số lượng luồng…

ManagementAgent.start – Bật JMX runtime:

jcmd 12345 ManagementAgent.start \
  jmxremote.port=8888 \
  jmxremote.authenticate=false \
  jmxremote.ssl=false

Hữu ích khi cần kết nối VisualVM hoặc JConsole mà chưa bật JMX từ đầu. Không nên tắt xác thực trong môi trường production.

6. Chẩn đoán chuyên sâu

VM.start_flight_recording – Kích hoạt ghi dữ liệu hiệu năng bằng Java Flight Recorder (JFR):

jcmd 12345 VM.start_flight_recording \
  name=DiagSession \
  settings=profile \
  duration=120s \
  filename=/tmp/diag.jfr

JFR có độ trễ thấp (<1%), phù hợp giám sát trực tiếp trên hệ thống thật.

VM.class_hierarchy (JDK 15+) – Xem cây kế thừa của một lớp:

jcmd 12345 VM.class_hierarchy java.util.ArrayList

Kỹ thuật sử dụng nâng cao

Áp dụng hàng loạt lên nhiều tiến trình:

for pid in $(jps | grep -E 'MyApp|Backend' | awk '{print $1}'); do
  jcmd $pid Thread.print > /tmp/stack-$pid.log
done

Theo dõi liên tục với watch:

watch -n 3 'jcmd 12345 PerfCounter.print | grep "sun.gc.collector.0.time"'

Sử dụng trong container:

  • Đảm bảo thư mục /tmp có quyền ghi.
  • Nếu gặp lỗi "Unable to open socket file", có thể dùng kill -3 <pid> để lấy thread dump.

Lưu ý quan trọng

  • Chỉ có thể attach vào tiến trình nếu cùng user hoặc là root.
  • Một số lệnh như GC.run hay VM.gc_class_histogram gây STW.
  • Tính năng Native Memory Tracking phải được kích hoạt từ lúc khởi động JVM.
  • JFR miễn phí trên OpenJDK từ phiên bản 11; Oracle JDK 8 yêu cầu giấy phép thương mại.

Bảng tra cứu nhanh

Mục đích Lệnh tương ứng
Lấy thread dump jcmd <pid> Thread.print -l
Xem tham số JVM jcmd <pid> VM.flags
Xem thuộc tính hệ thống jcmd <pid> VM.system_properties
Kích hoạt Full GC jcmd <pid> GC.run
Thống kê đối tượng sống jcmd <pid> VM.gc_class_histogram
Phân tích bộ nhớ native jcmd <pid> VM.native_memory summary
Bắt đầu ghi JFR jcmd <pid> VM.start_flight_recording ...
Mở JMX tại runtime jcmd <pid> ManagementAgent.start ...

Kết luận

jcmd đã trở thành tiêu chuẩn vàng trong việc chẩn đoán ứng dụng Java hiện đại nhờ khả năng tích hợp đa chức năng, mức độ can thiệp thấp và tương thích tốt với kiến trúc đám mây, container. Việc làm chủ jcmd giúp kỹ sư DevOps và SRE phản ứng nhanh hơn với sự cố hiệu năng, bộ nhớ hay tắc nghẽn luồng.

Thẻ: jcmd JVM Java diagnostics Performance Monitoring JFR

Đăng vào ngày 12 tháng 6 lúc 23:44