NullPointerException ใน Java ที่ไม่มี StackTrace


333

ฉันเคยมีอินสแตนซ์ของรหัส Java ของเราจับได้NullPointerExceptionแต่เมื่อฉันพยายามเข้าสู่ StackTrace (ซึ่งโดยทั่วไปแล้วจะเป็นการโทรThrowable.printStackTrace() ) สิ่งที่ฉันได้รับคือ:

java.lang.NullPointerException

มีคนอื่นเจอเรื่องนี้ไหม? ฉันลอง googling สำหรับ "java null pointer empty stack trace" แต่ก็ไม่เจออะไรแบบนี้


บริบทคืออะไร มีหลายหัวข้อที่เกี่ยวข้องหรือไม่? ฉันมีปัญหาในการพยายามติดตามสแต็กของข้อยกเว้นใน SwingWorker
Michael Myers

การทำเกลียวไม่เกี่ยวข้องที่นี่เพียงแค่ Java แบบเก่าธรรมดา
Edward Shtern

1
@Bozho - ไม่ - ไม่แน่ใจว่าจะทำซ้ำ NullPointer อย่างไร
Edward Shtern

1
เกี่ยวข้อง: stackoverflow.com/questions/1076191/…
Joshua Goldberg

ข้อมูลเพิ่มเติมเกี่ยวกับ-XX:-OmitStackTraceInFastThrowใน dup: stackoverflow.com/questions/4659151/…
Vadzim

คำตอบ:


407

คุณอาจใช้ HotSpot JVM (เดิมคือ Sun Microsystems ซึ่งซื้อในภายหลังโดย Oracle ซึ่งเป็นส่วนหนึ่งของ OpenJDK) ซึ่งดำเนินการเพิ่มประสิทธิภาพจำนวนมาก ในการรับร่องรอยสแต็กกลับมาคุณต้องผ่านตัวเลือก-XX:-OmitStackTraceInFastThrowไปยัง JVM

การปรับให้เหมาะสมคือเมื่อเกิดข้อยกเว้น (โดยทั่วไปคือ NullPointerException) เป็นครั้งแรกการติดตามสแต็กเต็มจะถูกพิมพ์และ JVM จะจดจำการติดตามสแต็ก (หรืออาจเป็นเพียงตำแหน่งของรหัส) เมื่อข้อยกเว้นนั้นเกิดขึ้นบ่อยครั้งเพียงพอการติดตามสแต็กจะไม่ถูกพิมพ์อีกต่อไปทั้งสองเพื่อให้ได้ประสิทธิภาพที่ดีขึ้นและไม่ทำให้บันทึกมีจำนวนสแต็กเหมือนกัน

เพื่อดูวิธีการนี้จะดำเนินการใน HotSpot JVM, คว้าสำเนาของมันOmitStackTraceInFastThrowและค้นหาตัวแปรโลก ครั้งสุดท้ายที่ฉันมองไปที่รหัส (ใน 2019) มันอยู่ในแฟ้มgraphKit.cpp


1
ขอบคุณสำหรับทิป. ความคิดใด ๆ หากมี gotchas ที่ซ่อนอยู่เพื่อผ่านตัวเลือกนี้ (ดูเหมือนว่าจะไม่มีพิษภัยตราบใดที่ใบสมัครของฉันไม่ได้มีข้อยกเว้นมากมาย)
Edward Shtern

ไม่มี gotchas ที่ซ่อนอยู่ที่ฉันรู้ เมื่อคุณดูซอร์สโค้ด Hotspot คุณจะเห็นว่าตัวเลือกนี้ใช้ในที่เดียวเท่านั้น (graphKit.cpp) และนั่นก็ดูดีสำหรับฉัน
Roland Illig

34
คิดว่าฉันจะเพิ่มบิตของข้อมูลเพิ่มเติมที่เมื่อการติดตามสแต็กได้รับการปรับปรุงให้ดีที่สุดนั่นเป็นเพราะมันได้รับการจัดการอย่างเต็มที่อย่างน้อยหนึ่งครั้ง: jawspeak.com/2010/05/26/…
sharakan

1
ฉันใช้ OpenJDK JVM เวอร์ชัน 1.8.0u171 (Debian 9) และดูเหมือนว่าจะยอมรับการ-XX:-OmitStackTraceInFastThrowตั้งค่าสถานะเช่นกัน ฉันยังไม่ได้ยืนยันว่าเป็นเพราะเหตุใดฉันจึงล้มเหลวในการพิมพ์ร่องรอยสแต็ก (เช่นใช้e.printStackTrace) แต่ดูเหมือนว่ามีแนวโน้มสูง ฉันได้ขยายคำตอบเพื่อสะท้อนการค้นพบนี้
Chris W.

ในกรณีของเราข้อยกเว้น 125 ข้อแรกมีการติดตามสแต็กจากนั้นส่วนที่เหลือใน 3 รอบของไฟล์บันทึกจะไม่มีเลย คำตอบนี้มีประโยชน์มากในการค้นหาผู้กระทำผิด
sukhmel

61

ตามที่คุณพูดถึงในความคิดเห็นคุณใช้ log4j ฉันค้นพบสถานที่ที่ฉันเขียน (โดยไม่ได้ตั้งใจ)

LOG.error(exc);

แทนแบบทั่วไป

LOG.error("Some informative message", e);

ผ่านความเกียจคร้านหรือบางทีก็ไม่ได้คิดถึงมัน ส่วนที่โชคร้ายของเรื่องนี้คือมันไม่ทำงานตามที่คุณคาดหวัง logger API ใช้วัตถุเป็นอาร์กิวเมนต์แรกไม่ใช่สตริง - จากนั้นจะเรียกไปที่ String () บนอาร์กิวเมนต์ ดังนั้นแทนที่จะได้รับการติดตามสแต็คที่ดีงามมันแค่พิมพ์ toString - ซึ่งในกรณีของ NPE นั้นค่อนข้างไร้ประโยชน์

บางทีนี่คือสิ่งที่คุณกำลังประสบอยู่?


1: นี้จะอธิบายอธิบายพฤติกรรมและคุณไม่ได้เป็นเพียงคนเดียวที่ค้นพบนี้ :)
ปีเตอร์แลง

4
จริง ๆ แล้วเรามีนโยบายมาตรฐานที่ไม่เคยใช้แบบฟอร์มแรกด้านบน (LOG.error (exc);) - เราใช้ลายเซ็นพารามิเตอร์ 2 เสมอเพื่อให้เราเพิ่มคำอธิบายที่อธิบายลงในบันทึกแทนที่จะเป็นเพียงสแต็กแบบสแต็กดิบ
Edward Shtern

5
แน่นอน แต่นโยบายไม่ได้หมายความว่าจะดำเนินการอย่างถูกต้องเสมอ! คิดว่ามันเป็นมูลค่าการกล่าวขวัญอย่างน้อย
Steven Schlansker

จริง แต่ในกรณีนี้มันเป็น ;-)
Edward Shtern

28

เราเคยเห็นพฤติกรรมแบบเดียวกันนี้ในอดีต มันกลับกลายเป็นว่าด้วยเหตุผลบางอย่างที่บ้าคลั่งหาก NullPointerException เกิดขึ้นที่สถานที่เดียวกันในรหัสหลาย ๆ ครั้งหลังจากใช้ไปสักพักLog.error(String, Throwable)จะหยุดรวมถึงร่องรอยสแต็กเต็ม

ลองดูเพิ่มเติมในบันทึกของคุณ คุณอาจพบผู้กระทำผิด

แก้ไข: ข้อผิดพลาดนี้ฟังดูเกี่ยวข้อง แต่ได้รับการแก้ไขนานแล้วมันอาจไม่ใช่สาเหตุ


2
ข้อผิดพลาดถูกปิด แต่แฟล็ก -XX: -OmitStackTraceInFastThrow ยังคงต้องการเพื่อแก้ไขปัญหาการปรับประสิทธิภาพให้เหมาะสม
Joshua Goldberg

ฉันได้เห็นสิ่งนี้เมื่อไม่นานมานี้ เบาะแสใด ๆ เกี่ยวกับสิ่งที่อาจเป็นสาเหตุของปัญหานี้หรือวิธีการแก้ไข ระบบการบันทึกอาจใช้เวลาหลายวันและสาเหตุที่แท้จริงหมุนออกไปไม่ต้องกังวลกับการค้นหาที่น่าเบื่อ ...
Pawel Veselov

5
Pawel คุณลองใช้-XX:-OmitStackTraceInFastThrowธง JVM ที่ Joshua แนะนำหรือยัง ดูเพิ่มเติมstackoverflow.com/a/2070568/6198
Matt Solnit

1
นี่มันสำหรับเรา ขอบคุณ
Andrew Cheong

20

นี่คือคำอธิบาย: ฮอตสปอตทำให้เกิดข้อยกเว้นสูญเสียการติดตามสแต็กในการผลิต - และการแก้ไข

ฉันทดสอบบน Mac OS X แล้ว

  • รุ่น java "1.6.0_26"
  • สภาพแวดล้อมรันไทม์ Java (TM) SE (บิลด์ 1.6.0_26-b03-383-11A511)
  • เซิร์ฟเวอร์ Java HotSpot (TM) 64- บิต VM (สร้าง 20.1-b02-383, โหมดผสม)

    Object string = "abcd";
    int i = 0;
    while (i < 12289) {
        i++;
        try {
            Integer a = (Integer) string;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

สำหรับรหัสเฉพาะส่วนนี้การทำซ้ำ 12288 (+ ความถี่?) น่าจะเป็นข้อ จำกัด ที่ JVM ตัดสินใจใช้ข้อยกเว้นที่จัดสรรล่วงหน้า ...


10

exception.toString ไม่ให้ StackTrace แก่คุณ แต่จะส่งคืนเท่านั้น

คำอธิบายสั้น ๆ ของการโยนได้นี้ ผลลัพธ์ที่ได้คือการต่อกันของ:

* the name of the class of this object
* ": " (a colon and a space)
* the result of invoking this object's getLocalizedMessage() method

ใช้exception.printStackTraceแทนการส่งออก StackTrace


ขออภัยฉันพลาดการโพสต์ต้นฉบับของฉัน ฉันบันทึกสิ่งเหล่านี้ผ่าน Log4J ซึ่งใช้ printStackTrace ()
Edward Shtern

1
คุณได้ลองใช้getStackTrace()เพื่อให้แน่ใจว่าปัญหาไม่ได้อยู่กับคนตัดไม้ของคุณ?
Peter Lang

1
หากคุณใช้ log4j ต้องแน่ใจว่าได้ส่งข้อยกเว้นเป็นส่วนหนึ่งของอาร์กิวเมนต์ไปยังวิธีการบันทึก ฉันจะโพสต์คำตอบด้วยสิ่งนั้น
Ravi Wallau

@raviaw จุดที่ถูกต้อง! @Edward Shtern: คุณยืนยันได้หรือไม่ว่าคุณกำลังใช้รูปแบบ 2-arg ของเมธอด log4j? ฉันรู้ว่าคุณกล่าวถึงในคำตอบเพิ่มเติมว่านโยบายของ บริษัท จะทำเช่นนั้น แต่คุณแน่ใจหรือไม่ว่าในกรณีนี้คุณปฏิบัติตามนโยบาย
KarstenF

อาจเป็นช็อตยาว แต่เป็นไปได้ไหมว่าข้อยกเว้นนั้นเกิดขึ้นในรหัสของบุคคลที่สาม บางทีมันอาจจะเป็น wrapper ยกเว้น (เขียนไม่ดี) ซึ่ง toString () เพียงแค่ส่งคืนชื่อคลาสของข้อยกเว้นที่ถูกล้อมรอบและซึ่งล้มเหลวในการจัดเตรียมการติดตามสแต็กพื้นฐาน ลองใส่บางสิ่งเช่น logger.info ("Exception class =" + exc.class.getCanonicalName ()) ลงใน catch block ของคุณและดูว่าคุณได้อะไร
KarstenF

4

คำแนะนำอื่น - หากคุณใช้ Eclipse คุณสามารถตั้งค่าเบรกพอยต์บน NullPointerException ได้เอง (ในมุมมอง Debug ไปที่แท็บ "จุดพัก" และคลิกที่ไอคอนเล็ก ๆ ที่มี!)

ตรวจสอบทั้งตัวเลือก "จับ" และ "ไม่ถูกจับ" - ตอนนี้เมื่อคุณทริกเกอร์ NPE คุณจะเบรกพอยต์ทันทีและจากนั้นคุณสามารถก้าวผ่านและดูว่ามันถูกจัดการอย่างไรและทำไมคุณถึงไม่ได้รับการติดตามสแต็ค


1

toString()คืนค่าชื่อข้อยกเว้นและข้อความทางเลือกเท่านั้น ฉันขอแนะนำให้โทร

exception.printStackTrace()

เพื่อทิ้งข้อความหรือถ้าคุณต้องการรายละเอียดเต็มไปด้วยเลือด:

 StackTraceElement[] trace = exception.getStackTrace()

ดูด้านบน - ฉันสะกดผิด - ฉันใช้ printStackTrace ()
Edward Shtern

1

(คำถามของคุณยังไม่ชัดเจนว่ารหัสของคุณโทรมาprintStackTrace()หรือกำลังดำเนินการโดยตัวจัดการการบันทึก)

ต่อไปนี้เป็นคำอธิบายที่เป็นไปได้เกี่ยวกับสิ่งที่อาจเกิดขึ้น:

  • ตัวบันทึก / การจัดการที่ใช้ถูกกำหนดค่าเพื่อส่งออกเฉพาะสตริงข้อความของข้อยกเว้นไม่ใช่การติดตามสแต็กเต็ม

  • แอปพลิเคชันของคุณ (หรือบางไลบรารีของบุคคลที่สาม) กำลังบันทึกข้อยกเว้นโดยใช้LOG.error(ex);แทนที่จะเป็นรูปแบบ 2 อาร์กิวเมนต์ของ (ตัวอย่าง) เมธอด log4j Logger

  • ข้อความมาจากที่อื่นที่คุณคิดว่าเป็น; เช่นมันกำลังจะมาถึงวิธีห้องสมุดของบุคคลที่สามหรือบางสิ่งที่สุ่มมาจากการพยายามแก้ไขข้อผิดพลาดก่อนหน้า

  • ข้อยกเว้นที่ถูกบันทึกไว้มีการโหลดวิธีการมากเกินไปเพื่อปิดบัง stacktrace หากเป็นเช่นนั้นข้อยกเว้นจะไม่ใช่ของแท้ NullPointerException แต่จะเป็นประเภทย่อยที่กำหนดเองของ NPE หรือแม้กระทั่งข้อยกเว้นบางส่วนที่ไม่ได้เชื่อมต่อ

ฉันคิดว่าคำอธิบายที่เป็นไปได้ครั้งสุดท้ายนั้นไม่น่าเป็นไปได้ แต่อย่างน้อยผู้คนคิดว่าทำอย่างนี้เพื่อ "ป้องกัน" วิศวกรรมย้อนกลับ แน่นอนว่ามันจะประสบความสำเร็จในการทำให้ชีวิตของผู้พัฒนาที่ซื่อสัตย์ซื่อสัตย์


1

เมื่อคุณใช้ AspectJ ในโครงการของคุณอาจเกิดขึ้นที่บางแง่มุมซ่อนส่วนของการติดตามสแต็ก ตัวอย่างเช่นวันนี้ฉันมี:

java.lang.NullPointerException:
  at com.company.product.MyTest.test(MyTest.java:37)

การติดตามสแต็กนี้ถูกพิมพ์เมื่อรันการทดสอบผ่านทาง Surefire ของ Maven

ในทางตรงกันข้ามเมื่อรันการทดสอบใน IntelliJ จะมีการพิมพ์การติดตามสแต็กที่แตกต่างกัน:

java.lang.NullPointerException
  at com.company.product.library.ArgumentChecker.nonNull(ArgumentChecker.java:67)
  at ...
  at com.company.product.aspects.CheckArgumentsAspect.wrap(CheckArgumentsAspect.java:82)
  at ...
  at com.company.product.MyTest.test(MyTest.java:37)

0

สิ่งนี้จะส่งออกข้อยกเว้นใช้เพื่อแก้ไขข้อบกพร่องคุณควรจัดการกับข้อยกเว้นที่ดีกว่า

import java.io.PrintWriter;
import java.io.StringWriter;
    public static String getStackTrace(Throwable t)
    {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        t.printStackTrace(pw);
        pw.flush();
        sw.flush();
        return sw.toString();
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.