เหตุใดจึงสามารถกู้คืนจาก StackOverflowError ได้


100

ฉันประหลาดใจที่เป็นไปได้ที่จะดำเนินการต่อแม้ว่าStackOverflowErrorจะเกิดขึ้นใน Java แล้วก็ตาม

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

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

public class Test
{
    public static void main(String[] args)
    {
        try {
            foo();
        } catch (StackOverflowError e) {
            bar();
        }
        System.out.println("normal termination");
    }

    private static void foo() {
        System.out.println("foo");
        foo();
    }

    private static void bar() {
        System.out.println("bar");
    }
}

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


4
ที่เกี่ยวข้อง: codegolf.stackexchange.com/questions/21114/…
ntoskrnl

57
ฉันทำข้อผิดพลาดบน StackOverflow ตลอดเวลา ไม่ได้หยุดฉันไม่ให้กลับมา

10
Yo dawg ... ฉันได้ยินมาว่าคุณชอบ stack overflow ดังนั้นเราจึงใส่ stack overflow ใน stackoverflow.com ของคุณ!
Pierre Henry

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

คำตอบ:


119

เมื่อสแตกล้นและStackOverflowErrorถูกโยนออกไปการจัดการข้อยกเว้นตามปกติจะคลายสแต็ก การคลายสแต็กหมายถึง:

  • ยกเลิกการดำเนินการของฟังก์ชันที่ใช้งานอยู่ในปัจจุบัน
  • ลบสแต็กเฟรมดำเนินการต่อด้วยฟังก์ชันการโทร
  • ยกเลิกการดำเนินการของผู้โทร
  • ลบสแต็กเฟรมดำเนินการต่อด้วยฟังก์ชันการโทร
  • และอื่น ๆ ...

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


1
@fge อย่าลังเลที่จะแก้ไขฉันคิดว่ามีการแบ่งย่อหน้า แต่ไม่พบสถานที่ที่ดูดี

1
คุณสามารถใช้สัญลักษณ์แสดงหัวข้อย่อย ... ฉันลังเลที่จะแก้ไขโพสต์ของคนอื่น;)
fge

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

2
@ เดลแนนฉันคิดว่าคำตอบนั้นไม่สมบูรณ์โดยไม่ต้องลงรายละเอียดว่าทำไมถึงเป็นความคิดที่ไม่ดี ความแตกต่างของข้อยกเว้นที่ถูกโยนทิ้งอย่างชัดเจนคือErrorไม่สามารถคาดการณ์ได้แม้ว่าจะเขียนโค้ดที่ปลอดภัยสำหรับข้อยกเว้นก็ตาม
Simon Richter

1
@SimonRichter ไม่คำถามค่อนข้างเฉพาะเจาะจง มันไม่เกี่ยวกับการจัดการErrors OP กำลังถามเกี่ยวกับเรื่องนี้เท่านั้นStackOverflowErrorและกำลังถามสิ่งที่เฉพาะเจาะจงเกี่ยวกับการจัดการข้อผิดพลาดนี้ : วิธีการเรียกใช้วิธีจะไม่ล้มเหลวเมื่อพบข้อผิดพลาดนี้
Bakuriu

23

เมื่อ StackOverflowError ถูกโยนสแต็กจะเต็ม อย่างไรก็ตามเมื่อถูกจับได้การfooโทรเหล่านั้นทั้งหมดจะถูกดึงออกจากสแต็ก barสามารถทำงานได้ตามปกติเนื่องจากสแต็กไม่ล้นด้วยfoos อีกต่อไป (โปรดทราบว่าฉันไม่คิดว่า JLS รับประกันว่าคุณจะสามารถกู้คืนจากสแตกล้นแบบนี้ได้)


12

เมื่อ StackOverFlow เกิดขึ้น JVM จะโผล่ลงมาที่จุดจับโดยปล่อยสแต็กออกมา

ในตัวอย่างของคุณมันกำจัดฟูที่ซ้อนกันทั้งหมด


8

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


1
โปรดระวังหากคุณทำเช่นนี้ว่าตัวจัดการข้อยกเว้นของคุณไม่ต้องการพื้นที่สแต็กมากกว่าที่มีอยู่!
Vince

2

ตามที่ได้รับคำตอบไปแล้วมันเป็นไปได้ที่จะรันโค้ดและโดยเฉพาะอย่างยิ่งในการเรียกใช้ฟังก์ชันหลังจากที่จับได้StackOverflowErrorเนื่องจากขั้นตอนการจัดการข้อยกเว้นตามปกติของ JVM จะคลายสแต็กระหว่างจุดthrowและcatchจุดโดยเพิ่มพื้นที่สแต็กเพื่อให้คุณใช้งานได้ และการทดสอบของคุณยืนยันว่าเป็นเช่นนั้น

อย่างไรก็ตามนั่นไม่เหมือนกับการบอกว่าโดยทั่วไปแล้วเป็นไปได้ที่จะกู้คืนจากไฟล์StackOverflowError.

StackOverflowErrorIS-A ซึ่งเป็น-AnVirtualMachineError Errorดังที่คุณชี้ให้เห็น Java ให้คำแนะนำที่คลุมเครือสำหรับError:

แสดงถึงปัญหาร้ายแรงที่ไม่ควรพยายามจับแอปพลิเคชันที่สมเหตุสมผล

และคุณมีเหตุผลสรุปได้ว่าควรจะดูเหมือนการจับErrorได้ในบางสถานการณ์ โปรดทราบว่าการทำการทดลองหนึ่งครั้งไม่ได้แสดงให้เห็นว่าโดยทั่วไปมีความปลอดภัยที่จะทำ เฉพาะกฎของภาษา Java และข้อกำหนดของคลาสที่คุณใช้เท่านั้นที่ทำได้ A VirtualMachineErrorเป็นคลาสพิเศษของข้อยกเว้นเนื่องจากJava Language SpecificationและJava Virtual Machine Specificationให้ข้อมูลเกี่ยวกับความหมายของข้อยกเว้นนี้ โดยเฉพาะอย่างยิ่งหลังพูดว่า :

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

...

  • StackOverflowError: การใช้งาน Java Virtual Machine ไม่มีพื้นที่สแต็กสำหรับเธรดโดยทั่วไปเนื่องจากเธรดกำลังดำเนินการเรียกใช้ซ้ำจำนวนที่ไม่ถูกผูกไว้อันเป็นผลมาจากข้อบกพร่องในโปรแกรมที่เรียกใช้งาน

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

ความไม่สามารถคาดเดาได้นี้อาจเป็นหายนะ เนื่องจากสามารถโยนลงในวิธีการหนึ่งมันอาจถูกโยนบางส่วนผ่านลำดับของการดำเนินการที่คลาสถือว่าเป็นการดำเนินการแบบ "อะตอม" โดยปล่อยให้อ็อบเจ็กต์อยู่ในสถานะที่แก้ไขบางส่วนไม่สอดคล้องกัน เมื่อวัตถุอยู่ในสถานะที่ไม่สอดคล้องกันความพยายามใด ๆที่จะใช้วัตถุนั้นอาจส่งผลให้เกิดพฤติกรรมที่ผิดพลาด ในกรณีที่การปฏิบัติทั้งหมดที่คุณไม่สามารถรู้ซึ่งวัตถุอยู่ในสถานะที่ไม่สอดคล้องกันดังนั้นคุณต้องคิดว่าไม่มีวัตถุเป็นที่น่าเชื่อถือ ใด ๆ ที่ดำเนินการกู้คืนหรือความพยายามที่จะดำเนินการต่อหลังจากข้อยกเว้นที่ถูกจับได้จึงอาจมีลักษณะการทำงานที่ผิดพลาด สิ่งเดียวที่ปลอดภัยที่ต้องทำคืออย่าจับไฟล์StackOverflowErrorแต่เป็นการอนุญาตให้โปรแกรมยุติ (ในทางปฏิบัติคุณอาจพยายามบันทึกข้อผิดพลาดบางอย่างเพื่อช่วยในการแก้ไขปัญหา แต่คุณไม่สามารถพึ่งพาการบันทึกนั้นได้อย่างถูกต้อง) นั่นคือคุณไม่สามารถกู้คืนไฟล์StackOverflowError .

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