1. JMX (Java Management Extensions): trái tim của giám sát JVM
Giám sát giống như kiểm tra sức khỏe định kỳ cho ứng dụng của bạn. Nếu ghi log có thể ví như cuốn nhật ký nơi ghi lại mọi thứ đã xảy ra, thì giám sát là tập hợp các thiết bị hiển thị trạng thái hệ thống ngay lúc này: nhiệt độ, nhịp tim, huyết áp, đường huyết và các chỉ số sống còn khác của JVM.
Nó giúp hiểu được bao nhiêu bộ nhớ thực sự đang được sử dụng và liệu có bị rò rỉ hay không, có bao nhiêu luồng đang chạy và trạng thái của chúng — đang chờ, đang thực thi hay bị khóa. Bạn có thể thấy tần suất bộ gom rác hoạt động, mức độ sử dụng CPU và kịp thời phát hiện các dấu hiệu đáng lo như mức bộ nhớ hoặc số lượng luồng tăng đột ngột.
Nói ngắn gọn, ghi log kể lại những gì đã xảy ra, còn giám sát cho thấy điều gì đang xảy ra ngay bây giờ.
Những điều cơ bản về JMX: nó là gì và để làm gì
JMX là công nghệ trong Java cho phép quan sát “đời sống” ứng dụng của bạn từ bên trong và thậm chí có thể điều khiển đôi chút. Nó hoạt động thông qua các đối tượng đặc biệt — MBean (Management Bean). Có thể coi chúng như “cảm biến” và “công tắc” bên trong JVM: cái thì hiển thị trạng thái hệ thống, cái thì cho phép bạn điều chỉnh đôi chút.
Với JMX, bạn có thể biết hiện JVM đã cấp phát bao nhiêu bộ nhớ, có bao nhiêu luồng đang hoạt động, mất bao nhiêu thời gian cho việc gom rác và nhiều điều khác — mà không cần chạm vào mã hay khởi động lại ứng dụng.
Cách nó hoạt động
Trong JVM, “mặc định” đã có nhiều MBean cung cấp thông tin về:
- bộ nhớ (heap, non-heap);
- bộ gom rác (GC);
- luồng;
- lớp (đã nạp bao nhiêu, đã dỡ bao nhiêu);
- và thậm chí về chính JVM (phiên bản, tham số khởi chạy).
JMX giống như bảng đồng hồ trên ô tô. Bạn thấy tốc độ, vòng tua, nhiệt độ động cơ — và tất cả đều sẵn qua các cảm biến chuẩn (MBean).
Cách truy cập JMX
Cách đơn giản nhất là sử dụng tiện ích chuẩn JConsole đi kèm JDK.
Khởi chạy JConsole
- Mở terminal/dòng lệnh.
- Chạy lệnh:
jconsole - Chọn tiến trình Java mà bạn muốn giám sát (ví dụ, ứng dụng của bạn).
JConsole sẽ kết nối tới JVM qua JMX và hiển thị biểu đồ cùng bảng: sử dụng bộ nhớ, số lượng luồng, hoạt động của bộ gom rác, v.v.
Ví dụ: xem bộ nhớ và luồng
Trong JConsole, mở các tab "Memory" và "Threads". Bạn sẽ thấy dung lượng bộ nhớ sử dụng thay đổi như thế nào, có bao nhiêu luồng đang tồn tại, luồng nào đang hoạt động và luồng nào đang chờ.
MBean tùy chỉnh
Bạn có thể tạo MBean tùy chỉnh để giám sát các metric riêng (ví dụ, số lượng đơn hàng đã xử lý). Nhưng đây là chủ đề nâng cao: các MBean chuẩn đã cung cấp rất nhiều thông tin.
2. VisualVM — giám sát trực quan và profiling
Nếu JConsole là “bảng điều khiển”, thì VisualVM là cả một trung tâm chẩn đoán với chụp X-quang, MRI và xét nghiệm máu. Nó cho phép không chỉ giám sát mà còn profile ứng dụng, tạo heap dump, phân tích rò rỉ và xem phương thức nào chiếm nhiều thời gian nhất.
Cài đặt và khởi chạy VisualVM
- VisualVM đi kèm JDK (thường là jvisualvm), nhưng có thể tải bản mới tại visualvm.github.io.
- Khởi chạy bằng lệnh:
jvisualvm - Sau khi mở, bạn sẽ thấy danh sách tất cả các tiến trình Java đang chạy trên máy của mình.
Kết nối tới tiến trình
- Tìm tiến trình của bạn (ví dụ, Main hoặc MyApp) trong danh sách.
- Nhấp đúp vào nó — một tab thông tin về tiến trình sẽ mở ra.
- Xong! Bây giờ bạn sẽ thấy:
- Sử dụng CPU và bộ nhớ theo thời gian thực.
- Số lượng luồng và trạng thái của chúng.
- Danh sách các lớp đã nạp.
- Khả năng tạo heap dump và thread dump.
Tính năng chính của VisualVM
Với VisualVM, bạn có thể quan sát cách bộ nhớ được sử dụng — cả heap và non-heap. Trên biểu đồ sẽ thấy mức tiêu thụ tăng hay ổn định. Để kiểm tra hoạt động của bộ gom rác, nhấn nút "Perform GC" — JVM sẽ ngay lập tức cố gắng dọn dẹp bộ nhớ. Bạn có thể tạo heap dump (ảnh chụp bộ nhớ) và nghiên cứu nó để tìm rò rỉ.
Công cụ cũng hiển thị trạng thái luồng: có bao nhiêu luồng chạy, luồng nào làm việc, chờ hoặc bị khóa. Nếu xảy ra deadlock, VisualVM sẽ giúp phát hiện và hiển thị các khóa chéo.
Để phân tích sâu có tính năng profiling: bạn sẽ thấy các phương thức “nóng” tiêu tốn nhiều thời gian CPU hoặc bộ nhớ nhất. Điều này hữu ích khi chẩn đoán sụt giảm hiệu năng bất ngờ.
Ví dụ: giám sát một ứng dụng nhỏ
public class MemoryLeakDemo {
public static void main(String[] args) throws InterruptedException {
List<byte[]> memoryConsumers = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
memoryConsumers.add(new byte[1024 * 1024]); // 1 MB
Thread.sleep(100); // Chờ một chút để biểu đồ mượt hơn
}
System.out.println("Xong! Đừng quên xem trong VisualVM :)");
Thread.sleep(60000); // Giữ ứng dụng mở để phân tích
}
}
Bây giờ:
- Chạy chương trình.
- Mở VisualVM, kết nối tới tiến trình.
- Quan sát mức sử dụng bộ nhớ tăng lên.
- Tạo heap dump, tìm các mảng chiếm bộ nhớ.
3. Java Flight Recorder (JFR): “hộp đen” cho JVM
JFR là gì
Java Flight Recorder là công cụ tích hợp trong JVM để thu thập chi tiết các sự kiện về hoạt động của ứng dụng. Đây là “hộp đen”: JFR ghi lại những gì xảy ra trong JVM để sau đó tìm điểm nghẽn hoặc nguyên nhân sự cố.
JFR thu thập:
- các sự kiện GC;
- thông tin về luồng;
- tần suất gọi phương thức và hồ sơ thời gian;
- các khoảng dừng và độ trễ;
- ngoại lệ và lỗi.
Cách bật JFR
Từ Java 11+, JFR có sẵn ngay trong OpenJDK.
Khởi chạy với JFR
java -XX:StartFlightRecording=filename=recording.jfr,duration=60s,settings=profile -jar MyApp.jar
- filename=recording.jfr — nơi lưu bản ghi.
- duration=60s — ghi trong bao lâu.
- settings=profile — mức độ chi tiết (default, profile, continuous).
Xem kết quả
Để phân tích tệp .jfr dùng JDK Mission Control (JMC):
- Tải JMC: jdk.java.net/jmc
- Mở tệp recording.jfr.
- Nghiên cứu biểu đồ, các phương thức “nóng”, các lần tạm dừng GC và hoạt động của luồng.
Ví dụ: ghi “hộp đen”
- Chạy ứng dụng với JFR (xem lệnh ở trên).
- Mở JDK Mission Control, tải tệp ghi.
- Đánh giá các “điểm nóng” về CPU/bộ nhớ, tần suất gom rác và số lượng luồng hoạt động.
4. Thực hành: giám sát ứng dụng
import java.util.ArrayList;
import java.util.List;
public class MonitoringExample {
public static void main(String[] args) throws InterruptedException {
List<byte[]> memory = new ArrayList<>();
for (int i = 0; i < 50; i++) {
memory.add(new byte[2 * 1024 * 1024]); // 2 MB
Thread.sleep(500);
}
// Khởi chạy một thread
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
System.out.println("Thread nền đang chạy...");
} catch (InterruptedException e) {
break;
}
}
}).start();
Thread.sleep(20000); // Giữ ứng dụng mở để giám sát
}
}
Thao tác:
- Chạy chương trình.
- Mở VisualVM, tìm tiến trình, xem biểu đồ bộ nhớ và luồng.
- Thử tạo heap dump và thread dump.
- Dành cho nâng cao: chạy với JFR và xem kết quả trong JMC.
5. Những lưu ý hữu ích
Bảng so sánh các công cụ giám sát
| Công cụ | Phù hợp cho | Cách khởi chạy | Đặc điểm |
|---|---|---|---|
| JConsole (JMX) | Các chỉ số cơ bản của JVM, luồng, GC | |
Đơn giản, đi kèm JDK |
| VisualVM | Giám sát, profiling, heap dump | |
Biểu đồ, phân tích bộ nhớ, CPU |
| Java Flight Recorder | Phân tích sâu các sự kiện JVM | |
Phân tích trong JMC, “hộp đen” |
| JDK Mission Control | Xem tệp JFR | ứng dụng riêng | Phân tích chi tiết, báo cáo |
Khuyến nghị
- Hãy giám sát không chỉ ở production mà cả ở giai đoạn thử nghiệm. Như vậy bạn sẽ sớm bắt được rò rỉ bộ nhớ và các luồng bị “treo”.
- Heap dump — không đáng sợ! Tạo ảnh chụp bộ nhớ khi nghi ngờ rò rỉ và phân tích nó trong VisualVM.
- Đừng ngại thử nghiệm với JFR. Dù chưa hiểu hết ngay, các sự kiện và biểu đồ sẽ giúp tìm điểm nghẽn.
- Có thể dùng JMX theo cách lập trình. Ví dụ, để gửi metric tới Prometheus/Grafana.
- Profiling — không dành cho chạy thường xuyên. Chỉ bật profiler có chọn lọc, nếu không ứng dụng có thể chậm lại.
6. Những lỗi thường gặp khi giám sát JVM
Lỗi số 1: Không giám sát gì cả. Nhiều người mới nghĩ rằng nếu ứng dụng “chạy được” thì mọi thứ đều ổn. Thực tế, vấn đề về bộ nhớ và luồng thường chỉ lộ ra dưới tải hoặc theo thời gian. Đừng ngại mở VisualVM/JConsole ít nhất là định kỳ.
Lỗi số 2: Tạo heap dump trên ứng dụng khổng lồ mà không chuẩn bị. Heap dump của ứng dụng lớn có thể nặng hàng GB và “đóng băng” tiến trình trong vài giây. Hãy làm việc này trong môi trường test hoặc vào giờ thấp điểm nếu ở production.
Lỗi số 3: Bỏ qua chỉ số GC và luồng. Tỷ lệ thời gian dành cho GC luôn cao là tín hiệu đáng báo động! Và nếu số lượng luồng tăng đều đặn — hãy tìm rò rỉ luồng.
Lỗi số 4: Không phân tích kết quả giám sát. Chỉ mở VisualVM là chưa đủ. Hãy xem đối tượng nào chiếm bộ nhớ, phương thức nào “ngốn” CPU, vì sao xuất hiện deadlock. Phân tích stack trace và nguyên nhân.
Lỗi số 5: Dùng profiler ở production mà không hiểu rõ. Profiling có thể làm ứng dụng chậm đi đáng kể. Chỉ bật để chẩn đoán, không phải “chạy thường trực”.
GO TO FULL VERSION