อะไรสามารถทำให้เกิดjava.lang.StackOverflowError
? งานพิมพ์สแต็กที่ฉันได้รับนั้นไม่ลึกมากนัก (มีเพียง 5 วิธีเท่านั้น)
อะไรสามารถทำให้เกิดjava.lang.StackOverflowError
? งานพิมพ์สแต็กที่ฉันได้รับนั้นไม่ลึกมากนัก (มีเพียง 5 วิธีเท่านั้น)
คำตอบ:
ตรวจสอบการเรียกซ้ำสำหรับวิธีการใด ๆ ส่วนใหญ่เกิดจากการเรียกใช้เมธอดแบบเรียกซ้ำ ตัวอย่างง่ายๆคือ
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
หนึ่งในอาร์กิวเมนต์ (ทางเลือก) ของ JVM คือขนาดสแต็ก มันคือ -Xss ฉันไม่รู้ว่าค่าเริ่มต้นคืออะไร แต่ถ้าจำนวนรวมของสิ่งต่างๆในสแต็กเกินค่านั้นคุณจะได้รับข้อผิดพลาดนั้น
โดยทั่วไปการเรียกซ้ำแบบไม่สิ้นสุดเป็นสาเหตุของสิ่งนี้ แต่ถ้าคุณเห็นสิ่งนั้นการติดตามสแต็กของคุณจะมีมากกว่า 5 เฟรม
ลองเพิ่มอาร์กิวเมนต์ a -Xss (หรือเพิ่มค่าหนึ่ง) เพื่อดูว่าสิ่งนี้หายไปหรือไม่
สิ่งที่ทำให้เกิด 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 มักจะแก้ไขได้ง่ายเมื่อเกิดขึ้น
ตามทฤษฎีแล้วยังเป็นไปได้ที่จะมีสแต็กล้นโดยไม่มีการเรียกซ้ำ แต่ในทางปฏิบัติดูเหมือนว่าจะเป็นเหตุการณ์ที่ค่อนข้างหายาก
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
มีพารามิเตอร์ของวิธีการเรียกค่าพารามิเตอร์ในท้องถิ่นของตนและที่อยู่ที่การกลับมาของวิธีการ ที่อยู่สำหรับส่งคืนหมายถึงจุดดำเนินการซึ่งการดำเนินการโปรแกรมจะดำเนินต่อไปหลังจากที่วิธีการเรียกคืนกลับ หากไม่มีช่องว่างสำหรับสแต็กเฟรมใหม่StackOverflowError
Java Virtual Machine (JVM) จะถูกโยนทิ้ง
กรณีที่พบบ่อยที่สุดที่อาจทำให้สแต็กของแอ็พพลิเคชัน Java หมดคือการเรียกซ้ำ ในการเรียกซ้ำเมธอดจะเรียกใช้ตัวเองในระหว่างการดำเนินการ Recursion
หนึ่งในเทคนิคการเขียนโปรแกรมเอนกประสงค์ที่ทรงพลังที่สุด แต่ต้องใช้ด้วยความระมัดระวังเพื่อStackOverflowError
หลีกเลี่ยง
เมื่อเรียกใช้ฟังก์ชันโดยแอ็พพลิเคชัน Java สแต็กเฟรมจะถูกจัดสรรบน call stack สแต็กเฟรมประกอบด้วยพารามิเตอร์ของเมธอดที่เรียกใช้พารามิเตอร์โลคัลและที่อยู่ส่งคืนของเมธอด
ที่อยู่สำหรับส่งคืนหมายถึงจุดดำเนินการซึ่งการดำเนินการโปรแกรมจะดำเนินต่อไปหลังจากที่วิธีการเรียกคืนกลับมา หากมีพื้นที่สำหรับกองกรอบใหม่นั้นStackOverflowErrorถูกโยนโดยโปรแกรม Java Virtual Machine (JVM)
กรณีที่พบบ่อยที่สุดที่อาจทำให้สแต็กของแอ็พพลิเคชัน Java หมดคือการเรียกซ้ำ
กรุณาดู
โซลูชันสำหรับผู้ใช้ไฮเบอร์เนตเมื่อแยกวิเคราะห์ข้อมูล:
ฉันมีข้อผิดพลาดนี้เนื่องจากฉันกำลังแยกวิเคราะห์รายการวัตถุที่แมปทั้งสองด้าน@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 ให้กับฟิลด์
ฉันสร้างโปรแกรมที่มีโหมดไฮเบอร์เนตซึ่งฉันสร้างคลาส POJO สองคลาสโดยทั้งสองมีอ็อบเจ็กต์ซึ่งกันและกันเป็นสมาชิกข้อมูล เมื่ออยู่ในวิธีการหลักฉันพยายามบันทึกไว้ในฐานข้อมูลฉันก็ได้รับข้อผิดพลาดนี้เช่นกัน
สิ่งนี้เกิดขึ้นเนื่องจากทั้งสองคลาสอ้างอิงซึ่งกันและกันดังนั้นการสร้างลูปซึ่งทำให้เกิดข้อผิดพลาดนี้
ดังนั้นตรวจสอบว่าความสัมพันธ์ประเภทนี้มีอยู่ในโปรแกรมของคุณหรือไม่
ข้อยกเว้นของ Stack Overflow อาจเกิดขึ้นได้เมื่อเธรดสแต็กมีขนาดใหญ่ขึ้นเรื่อย ๆ จนกระทั่งถึงขีด จำกัด สูงสุด
การปรับตัวเลือกขนาดกอง (Xss และ Xmso) ...
ฉันขอแนะนำให้คุณดูลิงค์นี้: http://www-01.ibm.com/support/docview.wss?uid=swg21162896 StackOverflowError มีหลายสาเหตุดังที่คุณเห็นในลิงค์ ....
ในกรณีของฉันฉันมีสองกิจกรรม ในกิจกรรมที่สองฉันลืมใส่ super ในเมธอด onCreate
super.onCreate(savedInstanceState);
StackOverflowError
แต่ฉันก็ไม่คิดว่ามันจะตอบคำถาม ฉันคิดว่าคำตอบที่เหมาะสมควรระบุวิธีอื่นในการรับข้อยกเว้นนี้มากกว่าการใช้การเรียกซ้ำมากเกินไปหรือบอกว่าไม่มีวิธีอื่นที่จะได้รับข้อยกเว้นดังกล่าวนอกจากการโยนด้วยตนเอง