มีวิธีการถ่ายโอนข้อมูลกองติดตามโดยไม่ทิ้งข้อยกเว้นใน java?


159

ฉันกำลังคิดที่จะสร้างเครื่องมือดีบั๊กสำหรับแอปพลิเคชัน Java ของฉัน

ฉันสงสัยว่าเป็นไปได้หรือไม่ที่จะได้รับการติดตามแบบสแต็กเหมือนException.printStackTrace()แต่ไม่มีข้อผิดพลาดเกิดขึ้นจริงหรือไม่

เป้าหมายของฉันคือการทิ้งสแต็กเพื่อดูว่าใครเป็นผู้โทร

คำตอบ:


84

คุณยังสามารถลองThread.getAllStackTraces()รับแผนที่การติดตามสแต็กสำหรับเธรดทั้งหมดที่ยังมีชีวิตอยู่


250

ใช่เพียงแค่ใช้

Thread.dumpStack()

44
... แต่อย่าโยนเลย
Rob

5
นั่นคือคำตอบที่ตอบคำถาม
bruno

7
ง่ายมากและสง่างาม นี่ควรเป็นคำตอบที่ยอมรับได้
deLock

11
Fwiw ซึ่งเป็นแหล่งที่มาของวิธีการนี้: public static void dumpStack() { new Exception("Stack trace").printStackTrace(); }
JohnK

นี่คือคำตอบที่ดีที่สุดสำหรับคำถามนี้หากคุณพอใจที่จะแสดงผลออกมาstderrซึ่งน่าจะเป็นความหมายของคำถาม
ไมค์หนู

46

หากคุณต้องการการติดตามสำหรับเธรดปัจจุบันเท่านั้น (แทนที่จะเป็นเธรดทั้งหมดในระบบตามที่คำแนะนำของ Ram ทำ) ให้ทำดังนี้:

Thread.currentThread () getStackTrace ()

ในการค้นหาผู้โทรให้ทำ:

private String getCallingMethodName() {
    StackTraceElement callingFrame = Thread.currentThread().getStackTrace()[4];
    return callingFrame.getMethodName();
}

และเรียกวิธีการนั้นจากภายในวิธีที่ต้องรู้ว่าใครเป็นผู้โทร อย่างไรก็ตามคำเตือน: ดัชนีของกรอบการโทรภายในรายการอาจแตกต่างกันไปตาม JVM! ทุกอย่างขึ้นอยู่กับจำนวนการโทรที่มีอยู่ใน getStackTrace ก่อนที่คุณจะไปถึงจุดที่สร้างการติดตาม โซลูชันที่มีประสิทธิภาพมากขึ้นจะได้รับการติดตามและวนซ้ำมันมองหาเฟรมสำหรับ getCallingMethodName จากนั้นทำสองขั้นตอนต่อไปเพื่อค้นหาผู้โทรที่แท้จริง


2
ดังนั้นเพียงแค่สร้างข้อยกเว้นใหม่ด้วยตัวคุณเองและใช้ [1] เป็นดัชนี!
Daniel

3
ชื่อของคำถามนี้บอกว่า "โดยไม่ทิ้งข้อยกเว้น" จริงอยู่ที่คุณแนะนำให้สร้างข้อยกเว้น แต่ไม่ทิ้ง แต่ฉันก็ยังคิดว่านั่นไม่ใช่จิตวิญญาณของคำถาม
Tom Anderson

1
แน่นอนมันเป็น การสร้างข้อยกเว้นเป็นวิธีที่ต่ำที่สุดในการรับ stacktrace แม้แต่ Thread.currentThread () ของคุณ getStackTrace () ก็ทำได้ แต่วิธีของฉันทำได้เร็วกว่า :)
Daniel

@Daniel มีสิ่งที่ต้องพิจารณาอีกมากมาย: ด้วยกรอบการทดสอบจำนวนมากเช่น Spock เพียงแค่สร้างข้อยกเว้น (โดยไม่ทิ้ง) จะเพียงพอสำหรับกรอบในการรับ: การทดสอบจะพิจารณาว่ามีข้อยกเว้น "เกิดขึ้น" และการทดสอบจะ จบด้วยความล้มเหลว
ไมค์หนู

@ Mikerodent: คุณแน่ใจเหรอ? สป็อคจะต้องใช้ตัวแทนในการทำเช่นนั้น แต่อย่างไรก็ตามเพราะฉันแค่ต้องการเรียกคลาสฉันใช้ Reflection.getCallerClass (2) ในฟังก์ชันยูทิลิตี้ คุณจะไม่ได้ฟังก์ชั่นและหมายเลขโทรศัพท์ในวิธีนั้น
Daniel

37

คุณสามารถรับการติดตามสแต็กดังนี้:

Throwable t = new Throwable();
t.printStackTrace();

หากคุณต้องการเข้าถึงเฟรมคุณสามารถใช้ t.getStackTrace () เพื่อรับอาร์เรย์ของเฟรมสแต็ก

ระวังว่า stacktrace นี้ (เหมือนกัน) อาจหายไปบางเฟรมหากคอมไพเลอร์ฮอตสปอตกำลังยุ่งอยู่กับการปรับสิ่งต่างๆให้เหมาะสม


ฉันชอบคำตอบนี้เพราะให้โอกาสฉันในการควบคุมผลลัพธ์ ฉันสามารถแทนที่ด้วยt.printStackTrace t.printStackTrace(System.out)

4
ในหนึ่งบรรทัด:new Throwable().printStackTrace(System.out);

1
นี่ควรเป็นคำตอบที่ยอมรับได้ มันง่ายและตรงไปตรงมา! ขอบคุณสำหรับการแบ่งปัน
Sam

2
และเป็นเรื่องง่ายที่จะส่งไปยัง java.util.Loggerlog.log(Level.SEVERE, "Failed to do something", new Throwable());
MitchBroadhead

13

ขอให้สังเกตว่า Thread.dumpStack () ส่งข้อยกเว้นจริง:

new Exception("Stack trace").printStackTrace();

12
มันสร้างข้อยกเว้น แต่ไม่ได้โยน
MatrixFrog

6

คุณยังสามารถส่งสัญญาณไปยัง JVM เพื่อดำเนินการ Thread.getAllStackTraces () บนกระบวนการ Java ที่กำลังรันอยู่โดยส่งสัญญาณ QUIT ไปยังกระบวนการ

บน Unix / Linux ใช้:

kill -QUIT process_idโดยที่ process_id คือหมายเลขกระบวนการของโปรแกรม Java ของคุณ

บน Windows คุณสามารถกด Ctrl-Break ในแอปพลิเคชันได้แม้ว่าโดยปกติคุณจะไม่เห็นสิ่งนี้เว้นแต่ว่าคุณกำลังใช้กระบวนการคอนโซล

JDK6 แนะนำตัวเลือกอื่นนั่นคือคำสั่ง jstack ซึ่งจะแสดงสแต็กจากกระบวนการ JDK6 ที่ทำงานอยู่บนคอมพิวเตอร์ของคุณ:

jstack [-l] <pid>

ตัวเลือกเหล่านี้มีประโยชน์มากสำหรับแอปพลิเคชันที่ทำงานในสภาพแวดล้อมการผลิตและไม่สามารถแก้ไขได้อย่างง่ายดาย มีประโยชน์อย่างยิ่งสำหรับการวินิจฉัยการหยุดชะงักของรันไทม์หรือปัญหาด้านประสิทธิภาพ

http://java.sun.com/developer/technicalArticles/Programming/Stacktrace/ http://java.sun.com/javase/6/docs/technotes/tools/share/jstack.html


6

หากคุณต้องการจับเอาท์พุท

StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();

6

Java 9 เปิดตัว StackWalkerคลาสและการสนับสนุนสำหรับการเดินสแต็ก

นี่เป็นตัวอย่างเล็ก ๆ น้อย ๆ จาก Javadoc:

walkวิธีการเปิดกระแสต่อเนื่องStackFramesสำหรับเธรดปัจจุบันและจากนั้นใช้ฟังก์ชั่นได้รับที่จะเดินStackFrameกระแส สตรีมรายงานองค์ประกอบเฟรมสแต็กตามลำดับจากเฟรมด้านบนส่วนใหญ่ที่แสดงถึงจุดดำเนินการที่สแต็กถูกสร้างไปที่เฟรมส่วนใหญ่ด้านล่าง StackFrameกระแสถูกปิดเมื่อคืนวิธีการเดินเท้า หากมีความพยายามที่จะนำสตรีมที่ปิดกลับมาใช้ใหม่IllegalStateExceptionจะถูกโยนทิ้ง

...

  1. ในการถ่ายภาพสแต็กเฟรม 10 อันดับแรกของเธรดปัจจุบัน

    List<StackFrame> stack = StackWalker.getInstance().walk(s ->
     s.limit(10).collect(Collectors.toList()));

0

HPROF Java Profiler

คุณไม่จำเป็นต้องทำสิ่งนี้ในรหัส คุณสามารถแนบ Java HPROF เข้ากับกระบวนการของคุณและเมื่อใดก็ตามที่กด Control- \ เพื่อส่งออก heap dump, เธรดที่กำลังรัน ฯลฯ . . โดยไม่ทำให้รหัสแอปพลิเคชันของคุณยุ่ง นี่ค่อนข้างล้าสมัยไปแล้ว Java 6 มาพร้อมกับ GUI jconsole แต่ฉันก็ยังพบว่า HPROF มีประโยชน์มาก


0

คำตอบโดย Rob Di Marcostderrอยู่ไกลโดยที่ดีที่สุดถ้าคุณมีความสุขที่จะส่งออกไปยัง

หากคุณต้องการจับภาพ a Stringโดยมีบรรทัดใหม่ตามการติดตามปกติคุณสามารถทำได้:

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.