jstack là công cụ chẩn đoán được tích hợp trong JDK, dùng để in ra thông tin stack trace của tất cả các thread (Thread Dump) trong một tiến trình Java. Đây là lệnh cốt lõi để gỡ lỗi các vấn đề như CPU tăng đột biến, deadlock, thread bị chặn, phản hồi chậm.
Cú pháp cơ bản
jstack [tùy chọn] <pid>
jstack [tùy chọn] <thực thi> <core>
jstack [tùy chọn] [id_server@]<địa chỉ IP hoặc hostname của server từ xa>
Cách dùng phổ biến nhất là:
jstack <pid>Trong đó<pid>là ID tiến trình Java thu được từjpshoặcps.
Các tùy chọn chi tiết
1. -l (chữ l thường) — Hiển thị thông tin khóa bổ sung (khuyến nghị mạnh)
jstack -l <pid>
- Ngoài stack thread, còn hiển thị:
- Thông tin về việc giữ và chờ khóa đồng bộ (monitor)
- Chi tiết khóa của
java.util.concurrent(nhưReentrantLock, khóa nội bộ củaThreadPoolExecutor) - Trong output sẽ xuất hiện:
- parking to wait for <0x000000076b8c3450> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
Mục đích: Xác định chính xác deadlock, nguyên nhân chờ thread.
Khuyến nghị: Nên luôn dùng
-ltrừ khi hiệu năng cực kỳ nhạy cảm.
2. -F (Force) — In ép buộc (khi tiến trình không phản hồi)
jstack -F <pid>
- Dùng khi JVM mục tiêu bị treo, không phản hồi yêu cầu Attach
- Bypass giao tiếp socket thông thường, sử dụng cơ chế ptrace ở cấp hệ điều hành để đọc bộ nhớ
- Điều kiện tiên quyết:
- Người dùng hiện tại có quyền
ptrace(trên Linux thường cầnroothoặc cùng user) - Hệ thống không vô hiệu hóa
ptrace(như Kubernetes cho phép mặc định, nhưng một số chính sách bảo mật sẽ hạn chế)
⚠️ Lưu ý:
- Trong chế độ
-Fsẽ không xuất thông tin khóa của-l - Có thể thất bại do quyền hạn hoặc hạn chế kernel (đặc biệt trong môi trường container, gVisor, Kata)
Kịch bản điển hình:
# jstack bình thường thất bại
jstack 12345
# Lỗi: Unable to open socket file...
# Thử ép buộc
jstack -F 12345
3. -m — Chế độ hỗn hợp (in native + Java stack) [đã lỗi thời/không khuyến nghị]
jstack -m <pid>
- Cố gắng in đồng thời stack Java + native (C/C++)
- Nhưng trong JDK hiện đại (JDK 8+) gần như vô dụng vì:
- HotSpot không giữ lại toàn bộ symbol native stack
- Cần JVM debug hoặc tùy chọn biên dịch đặc biệt
- Native frame thường hiển thị dưới dạng
[0x00007f...]không có ý nghĩa thực tế
Kết luận: Không dùng -m, thay vào đó dùng perf + async-profiler để phân tích native stack.
4. -h / -help — Hiển thị trợ giúp
jstack -help
In ra hướng dẫn sử dụng ngắn gọn.
Các tổ hợp không được hỗ trợ
Tổ hợpKết quả-F -lVô hiệu. Chế độ -F không hỗ trợ thông tin khóa-F -mVô hiệu. -F không hỗ trợ native stack-l -m️ Có thể thực thi, nhưng -m không có tác dụng thực tế
Cách dùng đúng chỉ có hai:
jstack -l <pid>→ Trường hợp bình thường, có thông tin khóajstack -F <pid>→ Khi tiến trình không phản hồi, ép buộc (không có thông tin khóa)
Phân tích định dạng output (các trường quan trọng)
Một đoạn thread dump điển hình:
"Worker-1" #8 prio=5 os_prio=0 tid=0x00007f8a1c000000 nid=0x303e runnable [0x00007f8a0dffe000]
java.lang.Thread.State: RUNNABLE
at com.example.Service.process(Service.java:42)
at com.example.Service.lambda$execute$0(Service.java:28)
- locked <0x000000076b8c3450> (a java.lang.Object)
- waiting to lock <0x000000076b8c3460> (a java.lang.Object) owned by "Worker-2" #9
TrườngÝ nghĩa"Worker-1"Tên thread (có thể đặt bằng Thread.setName())#8ID thread nội bộ JVMprio=5Mức ưu tiên Java (1~10)os_prio=0Mức ưu tiên hệ điều hànhtid=0x...Địa chỉ thread trong JVMnid=0x303eNative Thread ID (thập lục phân) → Dùng để đối chiếu với top -H``runnableTrạng thái thread (runnable, waiting, timed_waiting, blocked)java.lang.Thread.StateTrạng thái thread ở cấp Java- locked ...Khóa đang giữ- waiting to lock ... owned by "Worker-2"Khóa đang chờ và người giữ → Dấu hiệu deadlock!
Mẹo sử dụng thực chiến
1. Tìm thread tiêu thụ CPU cao (quy trình kinh điển)
# Bước 1: Tìm PID Java
jps -l
# Bước 2: Xem CPU từng thread
top -H -p <PID>
# Bước 3: Tìm ID thread CPU cao (thập phân), chuyển sang thập lục phân
printf "%x\n" <thread_id> # Ví dụ 12350 → 303e
# Bước 4: Lấy stack thread
jstack -l <PID> > threaddump.txt
# Bước 5: Tìm "nid=0x303e" trong dump
grep -A 30 "nid=0x303e" threaddump.txt
2. Phát hiện deadlock
jstack -l <PID>
Nếu có deadlock, đầu output sẽ chỉ rõ:
Found one Java-level deadlock:
=============================
"Worker-2":
waiting to lock monitor 0x00007f8a1c000000 (object 0x000000076b8c3450, a java.lang.Object),
which is held by "Worker-1"
"Worker-1":
waiting to lock monitor 0x00007f8a1c000001 (object 0x000000076b8c3460, a java.lang.Object),
which is held by "Worker-2"
3. Giải pháp thay thế trong container (khi jstack thất bại)
# Giải pháp 1: Dùng kill -3 (đáng tin cậy nhất!)
kill -3 <PID>
# Stack thread sẽ xuất ra stdout, trong container có thể xem qua kubectl logs
# Giải pháp 2: Dùng jcmd (hiện đại hơn)
jcmd <PID> Thread.print -l
kill -3không phụ thuộc vào cơ chế Attach, 100% hiệu quả!
4. Lấy liên tục (phân tích vấn đề không ổn định)
# Lấy mỗi 5 giây, tổng 3 lần
for i in {1..3}; do
jstack -l <PID> >> /tmp/threaddump.$(date +%s).txt
sleep 5
done
So sánh nhiều dump, có thể phát hiện thread luôn ở trạng thái RUNNABLE (có thể là vòng lặp vô hạn).
⚠️ ### Lỗi phổ biến và cách khắc phục
Lỗi 1:
Unable to open socket file: target process not responding or HotSpot VM not loaded
- Nguyên nhân: Cơ chế Attach thất bại (/tmp không ghi được, Alpine image, tiến trình treo)
- Giải pháp:
- Dùng
jstack -F - Hoặc dùng
kill -3 <PID> - Hoặc dùng
jcmd <PID> Thread.print
Lỗi 2:
Not enough space for virtual memory
- Nguyên nhân: Container thiếu bộ nhớ, không thể cấp phát buffer dump
- Giải pháp: Mở rộng bộ nhớ container trước, hoặc dùng trực tiếp
kill -3
Lỗi 3: Không có output hoặc output không đầy đủ
- Nguyên nhân: Khi redirect không dùng
-l, hoặc tiến trình thoát quá nhanh - Giải pháp: Đảm bảo tiến trình còn sống, dùng
jstack -l > file
jstack vs jcmd Thread.print
Đặc tínhjstack``jcmd <pid> Thread.printCần file AttachCóCóHỗ trợ -l thông tin khóaCóCần thêm -lCó được khuyến nghị chính thức️ Công cụ cũJDK 7+ khuyến nghịMở rộng chức năngCố địnhCó thể quản lý cùng các lệnh jcmd khácKhả năng tương thích containerKémTốt hơn
Khuyến nghị: Dự án mới ưu tiên dùng
jcmd, nhưngjstackvẫn tương thích rộng.
Tóm tắt best practices
- Mặc định thêm
-l:jstack -l <pid>để lấy thông tin khóa đầy đủ - Khi CPU cao, kết hợp
top -H+nidđể định vị - Trong container ưu tiên
kill -3 <pid>để tránh vấn đề Attach - Kiểm tra deadlock bằng "Found one Java-level deadlock" ở đầu dump
- Không dùng
-m, phân tích native stack dùngasync-profiler
Lệnh tham khảo nhanh
# Thread dump cơ bản (có khóa)
jstack -l 12345 > threaddump.log
# Dump ép buộc (tiến trình không phản hồi)
jstack -F 12345 > threaddump_force.log
# Giải pháp hiện đại (khuyến nghị)
jcmd 12345 Thread.print -l > threaddump_jcmd.log
# Đáng tin cậy nhất (thân thiện container)
kill -3 12345 # Log trong stdout
Nắm vững jstack, bạn sẽ có "máy X-quang" để nhìn thấu hành vi của thread Java!