สาเหตุ java.lang.StackOverflowError คืออะไร


87

อะไรสามารถทำให้เกิดjava.lang.StackOverflowError? งานพิมพ์สแต็กที่ฉันได้รับนั้นไม่ลึกมากนัก (มีเพียง 5 วิธีเท่านั้น)


3
โพสต์นี้อาจช่วยได้: stackoverflow.com/questions/860550/…
Jake Greene

คำตอบ:


60

ตรวจสอบการเรียกซ้ำสำหรับวิธีการใด ๆ ส่วนใหญ่เกิดจากการเรียกใช้เมธอดแบบเรียกซ้ำ ตัวอย่างง่ายๆคือ

public static void main(String... args) {
    Main main = new Main();

    main.testMethod(1);
}

public void testMethod(int i) {
    testMethod(i);

    System.out.println(i);
}

ที่นี่ System.out.println (i); จะถูกผลักซ้ำ ๆ ไปยังกองซ้อนเมื่อมีการเรียก testMethod


1
ฉันคิดว่าคุณถูก. แต่ทางออกของมันคืออะไร เนื่องจากเรากำลังสร้างวิธีการนำกลับมาใช้ใหม่หมายความว่าเราต้องการสิ่งนั้น เราไม่ต้องการเปลี่ยนแปลงวิธีการ แล้วจะแยกแยะข้อผิดพลาดนี้ได้อย่างไร?
Ajay Sharma

1
หรือคุณกำลังเข้าสู่วงวนที่ไม่มีที่สิ้นสุด!
ยาเลมัตตา

@yalematta วิธีการเรียกซ้ำควรมีเงื่อนไขในการออก ดังนั้นตรวจสอบว่าวิธีการเรียกซ้ำของคุณถูกนำไปใช้อย่างถูกต้องและเสร็จสิ้นขึ้นอยู่กับเงื่อนไข
Ayaz Alifov

@AjaySharma เราจำเป็นต้องออกแบบระบบของเราให้เหมาะสมกับขอบเขตหน่วยความจำที่เรากำหนดให้กับ JVM หากระบบทำงานผิดปกติด้วยข้อผิดพลาดต่อไปนี้เราจำเป็นต้องตรวจสอบฐานรหัสของเรา
โธตาศรีนาถ

22

หนึ่งในอาร์กิวเมนต์ (ทางเลือก) ของ JVM คือขนาดสแต็ก มันคือ -Xss ฉันไม่รู้ว่าค่าเริ่มต้นคืออะไร แต่ถ้าจำนวนรวมของสิ่งต่างๆในสแต็กเกินค่านั้นคุณจะได้รับข้อผิดพลาดนั้น

โดยทั่วไปการเรียกซ้ำแบบไม่สิ้นสุดเป็นสาเหตุของสิ่งนี้ แต่ถ้าคุณเห็นสิ่งนั้นการติดตามสแต็กของคุณจะมีมากกว่า 5 เฟรม

ลองเพิ่มอาร์กิวเมนต์ a -Xss (หรือเพิ่มค่าหนึ่ง) เพื่อดูว่าสิ่งนี้หายไปหรือไม่


10

สิ่งที่ทำให้เกิด java.lang.StackOverflowError โดยทั่วไปคือการเรียกซ้ำโดยไม่ได้ตั้งใจ สำหรับฉันบ่อยครั้งเมื่อฉันตั้งใจจะเรียก super method สำหรับ overidden method เช่นในกรณีนี้:

public class Vehicle {
    public void accelerate(float acceleration, float maxVelocity) {
        // set the acceleration
    }
}

public class SpaceShip extends Vehicle {
    @Override
    public void accelerate(float acceleration, float maxVelocity) {
        // update the flux capacitor and call super.accelerate
        // oops meant to call super.accelerate(acceleration, maxVelocity);
        // but accidentally wrote this instead. A StackOverflow is in our future.
        this.accelerate(acceleration, maxVelocity); 
    }
}

ประการแรกการทราบว่าเกิดอะไรขึ้นเบื้องหลังเมื่อเราเรียกใช้ฟังก์ชัน อาร์กิวเมนต์และที่อยู่ของตำแหน่งที่เรียกเมธอดนั้นถูกผลักลงบนสแต็ก (ดู http://en.wikipedia.org/wiki/Stack_(abstract_data_type)#Runtime_memory_management ) เพื่อให้เมธอดที่เรียกสามารถเข้าถึงอาร์กิวเมนต์และเมื่อ วิธีที่เรียกว่าเสร็จสมบูรณ์การดำเนินการสามารถดำเนินการต่อหลังจากการโทร แต่เนื่องจากเราเรียกสิ่งนี้ว่า speed (การเร่งความเร็ว maxVelocity) แบบวนซ้ำ (การเรียกซ้ำจะหลวมเมื่อเมธอดเรียกตัวเองดูข้อมูลเพิ่มเติมได้ที่http://en.wikipedia.org/wiki/Recursion_(computer_science)) เราอยู่ในสถานการณ์ที่เรียกว่าการเรียกซ้ำแบบไม่มีที่สิ้นสุดและเรายังคงรวบรวมอาร์กิวเมนต์และที่อยู่ที่ส่งคืนไว้ใน call stack เนื่องจาก call stack มีขนาด จำกัด ในที่สุดเราก็หมดพื้นที่ พื้นที่ว่างใน call stack เรียกว่า overflow นี่เป็นเพราะเราพยายามใช้พื้นที่สแต็กมากกว่าที่เรามีและข้อมูลก็ล้นสแต็กอย่างแท้จริง ในภาษาการเขียนโปรแกรม Java ผลลัพธ์นี้จะเกิดข้อยกเว้นรันไทม์ java.lang.StackOverflow และจะหยุดโปรแกรมทันที

ตัวอย่างข้างต้นค่อนข้างเรียบง่าย (แม้ว่ามันจะเกิดขึ้นกับฉันมากกว่าที่ฉันอยากจะยอมรับก็ตาม) สิ่งเดียวกันนี้อาจเกิดขึ้นได้ในหลาย ๆ ด้านเกี่ยวกับวิธีที่ทำให้ติดตามยากขึ้นเล็กน้อย อย่างไรก็ตามโดยทั่วไป StackOverflow มักจะแก้ไขได้ง่ายเมื่อเกิดขึ้น

ตามทฤษฎีแล้วยังเป็นไปได้ที่จะมีสแต็กล้นโดยไม่มีการเรียกซ้ำ แต่ในทางปฏิบัติดูเหมือนว่าจะเป็นเหตุการณ์ที่ค่อนข้างหายาก


8

คืออะไร java.lang.StackOverflowError

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

รายละเอียด

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

ตัวอย่าง

Minimal, Complete, and Verifiable Example :

package demo;

public class StackOverflowErrorExample {

    public static void main(String[] args) 
    {
        StackOverflowErrorExample.recursivePrint(1);
    }

    public static void recursivePrint(int num) {
        System.out.println("Number: " + num);

        if(num == 0)
            return;
        else
            recursivePrint(++num);
    }

}

เอาต์พุตคอนโซล

Number: 1
Number: 2
.
.
.
Number: 8645
Number: 8646
Number: 8647Exception in thread "main" java.lang.StackOverflowError
    at java.io.FileOutputStream.write(Unknown Source)
    at java.io.BufferedOutputStream.flushBuffer(Unknown Source)
    at java.io.BufferedOutputStream.flush(Unknown Source)
    at java.io.PrintStream.write(Unknown Source)
    at sun.nio.cs.StreamEncoder.writeBytes(Unknown Source)
    at sun.nio.cs.StreamEncoder.implFlushBuffer(Unknown Source)
    at sun.nio.cs.StreamEncoder.flushBuffer(Unknown Source)
    at java.io.OutputStreamWriter.flushBuffer(Unknown Source)
    at java.io.PrintStream.newLine(Unknown Source)
    at java.io.PrintStream.println(Unknown Source)
    at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:11)
    at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:16)
    .
    .
    .
    at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:16)

คำอธิบาย

เมื่อมีสายเรียกฟังก์ชั่นถูกเรียกโดยโปรแกรม Java เป็นกองกรอบการจัดสรรในสาย stack stack frameมีพารามิเตอร์ของวิธีการเรียกค่าพารามิเตอร์ในท้องถิ่นของตนและที่อยู่ที่การกลับมาของวิธีการ ที่อยู่สำหรับส่งคืนหมายถึงจุดดำเนินการซึ่งการดำเนินการโปรแกรมจะดำเนินต่อไปหลังจากที่วิธีการเรียกคืนกลับ หากไม่มีช่องว่างสำหรับสแต็กเฟรมใหม่StackOverflowErrorJava Virtual Machine (JVM) จะถูกโยนทิ้ง

กรณีที่พบบ่อยที่สุดที่อาจทำให้สแต็กของแอ็พพลิเคชัน Java หมดคือการเรียกซ้ำ ในการเรียกซ้ำเมธอดจะเรียกใช้ตัวเองในระหว่างการดำเนินการ Recursionหนึ่งในเทคนิคการเขียนโปรแกรมเอนกประสงค์ที่ทรงพลังที่สุด แต่ต้องใช้ด้วยความระมัดระวังเพื่อStackOverflowErrorหลีกเลี่ยง

อ้างอิง


4

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

ที่อยู่สำหรับส่งคืนหมายถึงจุดดำเนินการซึ่งการดำเนินการโปรแกรมจะดำเนินต่อไปหลังจากที่วิธีการเรียกคืนกลับมา หากมีพื้นที่สำหรับกองกรอบใหม่นั้นStackOverflowErrorถูกโยนโดยโปรแกรม Java Virtual Machine (JVM)

กรณีที่พบบ่อยที่สุดที่อาจทำให้สแต็กของแอ็พพลิเคชัน Java หมดคือการเรียกซ้ำ

กรุณาดู

วิธีแก้ StackOverflowError


3

โซลูชันสำหรับผู้ใช้ไฮเบอร์เนตเมื่อแยกวิเคราะห์ข้อมูล:

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

หากคุณอยู่ในสถานการณ์เดียวกันคุณสามารถแก้ปัญหานี้ได้โดยใช้@JsonManagedReferenceและ@JsonBackReferenceคำอธิบายประกอบ

คำจำกัดความจาก API:

  • JsonManagedReference ( https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonManagedReference.html ):

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

  • JsonBackReference: ( https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonBackReference.html ):

    คำอธิบายประกอบที่ใช้เพื่อระบุว่าคุณสมบัติที่เกี่ยวข้องเป็นส่วนหนึ่งของการเชื่อมโยงสองทางระหว่างฟิลด์ และบทบาทของมันคือลิงก์ "ลูก" (หรือ "ย้อนกลับ") ประเภทค่าของคุณสมบัติต้องเป็น bean: ไม่สามารถเป็น Collection, Map, Array หรือการแจงนับได้ การเชื่อมโยงได้รับการจัดการเพื่อให้คุณสมบัติที่มีคำอธิบายประกอบนี้ไม่ถูกทำให้เป็นอนุกรม และระหว่าง deserialization ค่าของมันจะถูกตั้งค่าเป็นอินสแตนซ์ที่มีลิงก์ "จัดการ" (ไปข้างหน้า)

ตัวอย่าง:

Owner.java:

@JsonManagedReference
@OneToMany(mappedBy = "owner", fetch = FetchType.EAGER)
Set<Car> cars;

Car.java:

@JsonBackReference
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "owner_id")
private Owner owner;

อีกวิธีหนึ่งคือการใช้@JsonIgnoreซึ่งจะตั้งค่า null ให้กับฟิลด์


2

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

สิ่งนี้เกิดขึ้นเนื่องจากทั้งสองคลาสอ้างอิงซึ่งกันและกันดังนั้นการสร้างลูปซึ่งทำให้เกิดข้อผิดพลาดนี้

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


1

ข้อยกเว้นของ Stack Overflow อาจเกิดขึ้นได้เมื่อเธรดสแต็กมีขนาดใหญ่ขึ้นเรื่อย ๆ จนกระทั่งถึงขีด จำกัด สูงสุด

การปรับตัวเลือกขนาดกอง (Xss และ Xmso) ...

ฉันขอแนะนำให้คุณดูลิงค์นี้: http://www-01.ibm.com/support/docview.wss?uid=swg21162896 StackOverflowError มีหลายสาเหตุดังที่คุณเห็นในลิงค์ ....


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

0

ในกรณีของฉันฉันมีสองกิจกรรม ในกิจกรรมที่สองฉันลืมใส่ super ในเมธอด onCreate

super.onCreate(savedInstanceState);

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