Họ đang đo gì
Bạn có quy trình không, có biết công cụ nào có sẵn trên máy production không, và có thu thập bằng chứng TRƯỚC khi khởi động lại không.
Trả lời ngắn~30 giây
Thu bằng chứng trước, sửa sau — vì khởi động lại xoá sạch manh mối. Mình chạy top -H -p <pid> để tìm luồng nào ăn CPU, đổi id luồng sang hex, rồi jstack <pid> và tìm nid=0x… tương ứng để biết luồng đó đang chạy gì. Song song đó jstat -gcutil để loại trừ khả năng là GC quay vòng chứ không phải code. Có hai thứ đó là đủ để phân biệt ba nguyên nhân phổ biến nhất.
Giải thích sâu
Ba nguyên nhân chiếm gần hết thực tế. Một là GC quay vòng: heap gần đầy, full GC chạy liên tục mà thu hồi được rất ít, CPU 100% nhưng ứng dụng gần như không tiến triển. Dấu hiệu là jstat cho thấy tỷ lệ old gen ở mức cao ổn định và số lần full GC tăng nhanh. Hai là vòng lặp vô hạn hoặc thuật toán bùng nổ trên một đầu vào bất thường, thấy rõ trong stack trace của đúng luồng đó. Ba là tranh chấp khoá làm nhiều luồng quay bận, thường lộ ra khi jstack cho thấy hàng chục luồng cùng ở một dòng.
Điều quan trọng về quy trình là chụp NHIỀU lần jstack cách nhau vài giây, không phải một lần. Một ảnh chụp cho bạn một khoảnh khắc; ba ảnh chụp cho bạn biết luồng nào KẸT ở cùng một chỗ — và đó mới là thứ đáng điều tra. Nếu nghi ngờ bộ nhớ thì jmap -dump:live trước khi khởi động lại, chấp nhận rằng nó gây một lần dừng dài, vì không có dump thì lần sau bạn lại đứng ở đúng chỗ này.
Ở mức senior, câu trả lời tốt còn nhắc tới thứ nên chuẩn bị TRƯỚC sự cố: bật Java Flight Recorder liên tục với chi phí thấp, để khi chuyện xảy ra bạn có sẵn dữ liệu vài phút trước đó thay vì phải bắt đầu đo lúc mọi thứ đang cháy. JFR bật sẵn tốn khoảng 1% và là một trong những đánh đổi dễ biện minh nhất trong vận hành JVM.
Và điều cuối: khởi động lại là hành động hợp lệ nếu người dùng đang chịu ảnh hưởng — chỉ cần làm sau khi đã lấy dump và thread dump. Ưu tiên khôi phục dịch vụ, nhưng đừng để việc khôi phục xoá luôn khả năng ngăn lần sau. Nói được thứ tự này là điểm cộng lớn hơn cả việc kể tên công cụ.
Mười phút đầu, theo thứ tự
top -H -p $PID # luồng nào ăn CPU? lấy TID thập phân
printf '%x\n' $TID # đổi sang hex để khớp với nid trong jstack
jstack $PID > /tmp/1.txt # chụp 3 lần, cách nhau 5 giây
jstat -gcutil $PID 1000 10 # O ổn định ở mức cao + FGC tăng = GC quay vòng
jcmd $PID GC.heap_info
jmap -dump:live,format=b,file=/tmp/heap.hprof $PID # trước khi restartCâu hỏi tiếp theo họ sẽ hỏi
?Không SSH được vào container thì sao?
Dùng kubectl exec nếu image có JDK; nhiều image chỉ có JRE nên không có jstack — đó là lý do nên đóng gói kèm công cụ chẩn đoán hoặc bật JFR sẵn. Còn không thì kubectl debug với ephemeral container chia sẻ process namespace.
?Làm sao biết là GC chứ không phải code?
Nhìn tên luồng trong top -H: luồng GC của G1 tên là GC Thread#n. Nếu chúng chiếm CPU thì là GC. Thêm nữa, GC log với -Xlog:gc* cho thấy tần suất và lượng thu hồi được — thu hồi ít mà chạy liên tục là dấu hiệu rõ ràng nhất.
Trả lời thế này là mất điểm
- “Khởi động lại rồi theo dõi tiếp.” Đó là hành động, không phải chẩn đoán, và nó xoá mọi bằng chứng.
- Chỉ nói tên công cụ mà không nói thứ tự và lý do. Người phỏng vấn muốn nghe quy trình.