เมื่อจับ java.lang.Error?


คำตอบ:


101

โดยทั่วไปไม่เคย

อย่างไรก็ตามบางครั้งคุณต้องตรวจจับข้อผิดพลาดที่เฉพาะเจาะจง

หากคุณกำลังเขียนโค้ด framework-ish (กำลังโหลดคลาสของบุคคลที่สาม) คุณควรจับ LinkageError (ไม่พบคลาส def ลิงค์ที่ไม่พอใจการเปลี่ยนคลาสที่เข้ากันไม่ได้)

ฉันยังเคยเห็นรหัสของบุคคลที่สามโง่ ๆ ที่ขว้างคลาสย่อยของ Errorดังนั้นคุณจะต้องจัดการกับสิ่งเหล่านั้นด้วย

อย่างไรก็ตามฉันไม่แน่ใจว่าจะไม่สามารถกู้คืนOutOfMemoryErrorได้


3
ที่ฉันต้องทำเพื่อโหลด DLL นั้นจะล้มเหลวหากไม่ได้กำหนดค่าอย่างถูกต้อง ไม่ใช่ข้อผิดพลาดร้ายแรงในกรณีของแอปพลิเคชันนี้
Mario Ortegón

7
บางครั้งการตรวจจับ OutOfMemoryError ก็สมเหตุสมผลเช่นเมื่อคุณสร้างรายการอาร์เรย์ขนาดใหญ่
SpaceTrucker

3
@SpaceTrucker: วิธีการดังกล่าวทำงานได้ดีในแอปพลิเคชันแบบมัลติเธรดหรือมีความเสี่ยงอย่างมากที่การจัดสรรขนาดเล็กในเธรดอื่นล้มเหลวเพราะเหตุนี้? …สันนิษฐานได้ก็ต่อเมื่ออาร์เรย์ของคุณมีขนาดเล็กพอที่จะจัดสรรได้ แต่ไม่เหลืออะไรให้ใครอีก
PJTraill

@PJTraill ฉันไม่แน่ใจเกี่ยวกับเรื่องนั้น ซึ่งจะต้องมีตัวอย่างทางสถิติในโลกแห่งความเป็นจริง ฉันคิดว่าฉันเคยเห็นรหัสดังกล่าว แต่จำไม่ได้ว่าอยู่ที่ไหน
SpaceTrucker

51

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

http://pmd.sourceforge.net/rules/strictexception.html


27
อย่าพูดไม่เคย เรามีรหัสทดสอบที่ "ยืนยันว่าเป็นเท็จ" จากนั้นจับ AssertionError เพื่อให้แน่ใจว่าตั้งค่าแฟล็ก -ea นอกเหนือจากนั้น ... ใช่อาจจะไม่เคย ;-)
Outlaw Programmer

3
วิธีการเกี่ยวกับแอ็พพลิเคชันเซิร์ฟเวอร์ที่ส่งคำขอไปยังเธรดของผู้ปฏิบัติงาน อาจไม่สมเหตุสมผลที่จะจับ Throwable บนเธรดของผู้ปฏิบัติงานเพื่อดักจับข้อผิดพลาดใด ๆ และอย่างน้อยก็ลองบันทึกสิ่งที่ผิดพลาดหรือไม่?
Leigh

11
ไม่เคย ... ยกเว้นเมื่อคุณต้องการจริงๆ ไม่เคยเป็นคำที่หนักแน่นและมีข้อยกเว้นสำหรับกฎเสมอ หากคุณกำลังสร้างเฟรมเวิร์กก็ไม่น่าเป็นไปได้ที่คุณจะต้องจับและจัดการข้อผิดพลาดบางอย่างแม้ว่าจะเป็นเพียงการบันทึกก็ตาม
Robin

แล้วข้อผิดพลาดเช่น NoSuchMethodError ที่มาจากวิธีการไลบรารีของบุคคลที่สามล่ะ
ha9u63ar

@OutlawProgrammer สำหรับการบันทึกมีวิธีอื่น ๆ ในการทดสอบแบบเดียวกัน:boolean assertionsEnabled = false; assert assertionsEnabled = true;
shmosel

16

โดยทั่วไปคุณควรจับjava.lang.Errorและเขียนลงในบันทึกหรือแสดงให้ผู้ใช้เห็นเสมอ ฉันทำงานในฝ่ายสนับสนุนและเห็นทุกวันว่าโปรแกรมเมอร์ไม่สามารถบอกได้ว่าเกิดอะไรขึ้นในโปรแกรม

หากคุณมีเธรด daemon คุณต้องป้องกันไม่ให้ถูกยกเลิก ในกรณีอื่น ๆ แอปพลิเคชันของคุณจะทำงานได้อย่างถูกต้อง

คุณควรจับjava.lang.Errorที่ระดับสูงสุดเท่านั้น

หากคุณดูรายการข้อผิดพลาดคุณจะเห็นว่าส่วนใหญ่สามารถจัดการได้ ตัวอย่างเช่นกZipErrorเกิดขึ้นในการอ่านไฟล์ zip ที่เสียหาย

ข้อผิดพลาดที่พบบ่อยที่สุดคือOutOfMemoryErrorและNoClassDefFoundErrorซึ่งในกรณีส่วนใหญ่ปัญหารันไทม์

ตัวอย่างเช่น:

int length = Integer.parseInt(xyz);
byte[] buffer = new byte[length];

สามารถผลิตไฟล์ OutOfMemoryErrorแต่ปัญหารันไทม์และไม่มีเหตุผลที่จะยุติโปรแกรมของคุณ

NoClassDefFoundErrorส่วนใหญ่เกิดขึ้นหากไม่มีไลบรารีหรือถ้าคุณทำงานกับ Java เวอร์ชันอื่น หากเป็นส่วนเสริมของโปรแกรมคุณไม่ควรยุติโปรแกรมของคุณ

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


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

OutOfMemoryErrorไม่ใช่ข้อผิดพลาดรันไทม์ไม่มีการรับประกันว่าแอปพลิเคชันจะสามารถกู้คืนได้ หากคุณโชคดีคุณอาจได้รับ OOM new byte[largeNumber]แต่ถ้าการจัดสรรนั้นไม่เพียงพอที่จะทำให้เกิด OOM ก็อาจถูกทริกเกอร์ในบรรทัดถัดไปหรือเธรดถัดไป ปัญหานี้เป็นปัญหารันไทม์เพราะถ้าเป็นการป้อนข้อมูลที่ไม่น่าเชื่อถือก็ควรจะตรวจสอบก่อนที่จะเรียกlength new byte[]
Jeeyoung Kim

NoClassDefFoundErrorสามารถเกิดขึ้นได้ทุกที่เนื่องจากมีการเรียกใช้เมื่อโค้ดจาวาที่คอมไพล์ไม่พบคลาส หาก JDK ของคุณกำหนดค่าไม่ถูกต้องอาจทำให้เกิดการพยายามใช้java.util.*คลาสและแทบจะเป็นไปไม่ได้ที่จะตั้งโปรแกรมต่อต้าน หากคุณเป็นทางเลือกที่จะรวมการพึ่งพาคุณควรใช้ClassLoaderเพื่อตรวจสอบว่ามีอยู่หรือไม่ซึ่งเกิดClassNotFoundExceptionขึ้น
Jeeyoung Kim

1
ZipErrorบ่งชี้ว่าไฟล์ jar ที่มีคลาสเป็นไฟล์ zip ที่เสียหาย นี่เป็นปัญหาที่ค่อนข้างร้ายแรงและ ณ จุดนี้คุณไม่สามารถเชื่อถือโค้ดใด ๆ ที่ถูกเรียกใช้งานได้และจะเป็นเรื่องที่ขาดความรับผิดชอบสำหรับการพยายาม "กู้คืน" จากโค้ด
Jeeyoung Kim

2
โดยทั่วไปการจับjava.lang.Errorหรือjava.lang.Throwableอยู่ในระดับบนสุดอาจเป็นประโยชน์และพยายามทำอะไรบางอย่างกับมันเช่นบันทึกข้อความแสดงข้อผิดพลาด แต่ ณ จุดนั้นไม่มีการรับประกันว่าสิ่งนี้จะถูกดำเนินการ หาก JVM ของคุณเป็น OOMing การพยายามบันทึกอาจจัดสรรจำนวนมากขึ้นStringซึ่งจะทริกเกอร์ OOM อื่น
Jeeyoung Kim

15

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

ตัวอย่างเช่นโดยปกติการวนซ้ำควรเป็น:

try {
   while (shouldRun()) {
       doSomething();
   }
}
catch (Throwable t) {
   log(t);
   stop();
   System.exit(1);
}

แม้ว่าในบางกรณีคุณจะต้องการจัดการข้อผิดพลาดที่แตกต่างกันออกไปเช่นใน OutOfMemoryError คุณจะสามารถปิดแอปพลิเคชันได้เป็นประจำ (แม้อาจจะทำให้หน่วยความจำว่างและดำเนินการต่อ) ในบางกรณีคุณก็ไม่สามารถทำได้มากนัก


1
จับOutOfMemoryError และต่อเนื่องมากกว่าที่มีอยู่ทันทีเป็นเรื่องโง่เพราะโปรแกรมของคุณนั้นอยู่ในสภาพที่ไม่ได้กำหนด
Raedwald

1
ระบบการโทรการออกจากการจับผู้ขว้างมีผลที่ไม่ได้ตั้งใจจากการฆ่าทุกสิ่งที่ทำงานบน JVM ไม่ใช่เฉพาะแอปพลิเคชันที่เป็นปัญหา โดยปกติไม่ใช่แนวทางปฏิบัติที่ดีสำหรับเว็บแอปพลิเคชันที่อาจโฮสต์บนเซิร์ฟเวอร์แอปร่วมกับเว็บแอปพลิเคชันอื่น ๆ (ไม่ต้องพูดถึงว่าจะส่งผลกระทบต่อเซิร์ฟเวอร์ของแอปเอง)
Andrew Norman

9

น้อยครั้งมาก

ฉันจะพูดเฉพาะที่ระดับบนสุดของเธรดเพื่อให้ ATTEMPT ส่งข้อความพร้อมเหตุผลที่เธรดกำลังจะตาย

หากคุณอยู่ในกรอบที่ทำสิ่งนี้ให้คุณปล่อยให้มันอยู่ในกรอบ


6

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


6

โดยErrorปกติแล้วไม่ควรถูกจับเนื่องจากบ่งบอกถึงภาวะผิดปกติที่ไม่ควรเกิดขึ้นบ่งชี้ว่าสภาพผิดปกติที่ไม่ควรเกิดขึ้น

จากข้อกำหนด Java API สำหรับErrorคลาส:

An Errorเป็นคลาสย่อยThrowable ที่บ่งบอกถึงปัญหาร้ายแรงที่แอปพลิเคชันที่สมเหตุสมผลไม่ควรพยายามจับ ข้อผิดพลาดดังกล่าวส่วนใหญ่เป็นภาวะผิดปกติ [ ... ]

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

ตามที่ระบุไว้ an Errorจะถูกโยนในสถานการณ์ที่เป็นโอกาสเท่านั้นเมื่อErrorเกิดขึ้นมีแอปพลิเคชันน้อยมากที่สามารถทำได้และในบางสถานการณ์ Java Virtual Machine เองอาจอยู่ในสถานะที่ไม่เสถียร (เช่นVirtualMachineError)

แม้ว่า an Errorจะเป็นคลาสย่อยThrowableซึ่งหมายความว่าสามารถจับได้โดยtry-catchอนุประโยค แต่ก็อาจไม่จำเป็นจริงๆเนื่องจากแอปพลิเคชันจะอยู่ในสถานะผิดปกติเมื่อError JVM ถูกโยนทิ้ง

นอกจากนี้ยังมีส่วนสั้น ๆ ในหัวข้อนี้ในมาตรา11.5 ยกเว้นลำดับชั้นของJava Language ข้อกำหนดฉบับที่


6

หากคุณบ้าพอที่จะสร้างกรอบการทดสอบหน่วยใหม่นักวิ่งทดสอบของคุณอาจต้องจับ java.lang.AssertionError ที่เกิดจากกรณีทดสอบใด ๆ

มิฉะนั้นให้ดูคำตอบอื่น ๆ


5

และยังมีอีกสองกรณีที่หากคุณพบข้อผิดพลาดคุณจะต้องทำการแก้ไขใหม่ ตัวอย่างเช่นไม่ควรจับThreadDeathอาจทำให้เกิดปัญหาใหญ่ได้คือคุณจับมันในสภาพแวดล้อมที่มีอยู่ (เช่นแอปพลิเคชันเซิร์ฟเวอร์):

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


2
ซึ่งจริงๆแล้วไม่ใช่ปัญหาเพราะคุณไม่จับErrors
Bombe

4

มากน้อยมาก

ฉันทำสำหรับกรณีที่รู้จักกันเฉพาะเจาะจงมากเพียงกรณีเดียว ตัวอย่างเช่น java.lang.Uns พอใจLinkErrorสามารถโยนได้หากClassLoaderสองตัวโหลด DLL เดียวกัน (ฉันยอมรับว่าฉันควรย้าย JAR ไปยัง classloader ที่ใช้ร่วมกัน)

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

แม้แต่โปรแกรมเมอร์ใน C / C ++ พวกเขาก็แสดงข้อผิดพลาดและบอกสิ่งที่คนไม่เข้าใจก่อนที่จะออก (เช่นหน่วยความจำล้มเหลว)


3

ในการประยุกต์ใช้ Android ฉันกำลังจับjava.lang.VerifyError ไลบรารีที่ฉันใช้จะไม่ทำงานในอุปกรณ์ที่มีระบบปฏิบัติการเวอร์ชันเก่าและรหัสไลบรารีจะทำให้เกิดข้อผิดพลาดดังกล่าว แน่นอนว่าฉันสามารถหลีกเลี่ยงข้อผิดพลาดได้โดยการตรวจสอบเวอร์ชันของระบบปฏิบัติการที่รันไทม์ แต่:

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

3

มันค่อนข้างสะดวกในการจับ java.lang.AssertionError ในสภาพแวดล้อมการทดสอบ ...


คุณพยายามเพิ่มคุณค่าอะไร
lpapp

การเพิ่มมูลค่าของชุดทดสอบที่ไม่ยกเลิก
Jono

2

ตามหลักการแล้วเราไม่ควรจัดการ / จับข้อผิดพลาด แต่อาจมีบางกรณีที่เราจำเป็นต้องทำตามข้อกำหนดของเฟรมเวิร์กหรือแอปพลิเคชัน สมมติว่าฉันมี XML Parser daemon ซึ่งใช้DOM Parserซึ่งใช้หน่วยความจำมากกว่า หากมีข้อกำหนดเช่น Parser thread ไม่ควรตายเมื่อได้รับOutOfMemoryErrorควรจัดการและส่งข้อความ / เมลไปยังผู้ดูแลระบบแอปพลิเคชัน / เฟรมเวิร์ก


1

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


1

อาจเป็นการเหมาะสมที่จะตรวจจับข้อผิดพลาดภายในการทดสอบหน่วยที่ตรวจสอบการยืนยัน หากมีคนปิดใช้งานการยืนยันหรือลบการยืนยันที่คุณต้องการทราบ


1

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

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

คุณจะปรับลดระดับความสามารถในการอ่านโค้ดของคุณด้วย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.