StackOverflowError คืออะไร


439

อะไรคือStackOverflowErrorสาเหตุอะไรและฉันควรจัดการกับพวกเขาอย่างไร


ขนาดสแต็กใน java มีขนาดเล็ก และบางครั้งเช่นการโทรซ้ำหลายครั้งคุณประสบปัญหานี้ คุณสามารถออกแบบรหัสของคุณซ้ำได้ คุณสามารถหารูปแบบการออกแบบทั่วไปที่จะทำใน url นี้: jndanial.com/73
JNDanial

วิธีหนึ่งที่ไม่ชัดเจนในการรับ: เพิ่มบรรทัดnew Object() {{getClass().newInstance();}};ในบริบทแบบสแตติก (เช่นmainวิธีการ) ไม่ทำงานจากบริบทอินสแตนซ์ (พ่นเท่านั้นInstantiationException)
John McClane

คำตอบ:


408

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

กระบวนการของคุณยังมีฮีปซึ่งอยู่ที่ส่วนล่างสุดของกระบวนการของคุณ ในขณะที่คุณจัดสรรหน่วยความจำฮีปนี้สามารถขยายไปยังส่วนท้ายของพื้นที่ที่อยู่ของคุณ อย่างที่คุณเห็นมีความเป็นไปได้ที่ฮีปจะ"ชนกัน"กับกองซ้อน (เหมือนแผ่นเปลือกโลก !!!)

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

อย่างไรก็ตามด้วยการเขียนโปรแกรม GUI คุณสามารถสร้างการสอบถามซ้ำทางอ้อมการเรียกซ้ำทางอ้อมตัวอย่างเช่นแอปของคุณอาจจัดการกับข้อความสีและในขณะประมวลผลพวกเขาอาจเรียกใช้ฟังก์ชั่นที่ทำให้ระบบส่งข้อความสีอื่น ที่นี่คุณไม่ได้เรียกตัวเองอย่างชัดเจน แต่ OS / VM ได้ทำเพื่อคุณแล้ว

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

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


1
โปสเตอร์ต้นฉบับ: เฮ้นี่มันเยี่ยมมาก ดังนั้นการเรียกซ้ำจึงเป็นหน้าที่ของกองซ้อนล้นอยู่เสมอ หรือสิ่งอื่น ๆ สามารถรับผิดชอบได้เช่นกัน? น่าเสียดายที่ฉันกำลังใช้ห้องสมุด ... แต่ไม่ใช่ที่ฉันเข้าใจ
Ziggy

4
ฮ่าฮ่าฮ่าดังนั้นที่นี่คือ: ในขณะที่ (คะแนน <100) {addMouseListeners (); moveball (); checkforcollision (); หยุดชั่วคราว (ความเร็ว);} ว้าวฉันรู้สึกง่อยเพราะไม่รู้ว่าฉันจะจบลงด้วยการฟังเมาส์แบบกองซ้อน ... ขอบคุณพวก!
Ziggy

4
ไม่ล้นสแต็คยังสามารถมาจากตัวแปรเป็นขนาดใหญ่เกินไปที่จะจัดสรรในกองถ้าคุณมองขึ้นบทความวิกิพีเดียได้ที่en.wikipedia.org/wiki/Stack_overflow
JB King

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

3
@JB King: ใช้ไม่ได้กับ Java จริงๆมีเพียงประเภทและการอ้างอิงดั้งเดิมเท่านั้นที่ถูกเก็บไว้ในสแต็ก ทุกสิ่งที่ยิ่งใหญ่ (อาร์เรย์และวัตถุ) อยู่บนกอง
jcsahnwaldt พูดว่า GoFundMonica

107

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

ตัวแปรโลคัลถูกเก็บในสแต็ก : ป้อนคำอธิบายรูปภาพที่นี่

หากคุณดูรูปคุณควรเข้าใจว่าสิ่งต่าง ๆ ทำงานอย่างไร

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

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

ตัวอย่างของการขว้าง StackOverflowErrorแสดงอยู่ด้านล่าง:

StackOverflowErrorExample.java:

public class StackOverflowErrorExample {

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

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

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

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

ตัวอย่างการดำเนินการโดยใช้ -Xss1Mแฟล็กที่ระบุขนาดของสแตกเธรดเท่ากับ 1MB จะแสดงด้านล่าง:

Number: 1
Number: 2
Number: 3
...
Number: 6262
Number: 6263
Number: 6264
Number: 6265
Number: 6266
Exception in thread "main" java.lang.StackOverflowError
        at java.io.PrintStream.write(PrintStream.java:480)
        at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
        at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
        at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)
        at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
        at java.io.PrintStream.write(PrintStream.java:527)
        at java.io.PrintStream.print(PrintStream.java:669)
        at java.io.PrintStream.println(PrintStream.java:806)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:4)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
        ...

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

วิธีจัดการกับ StackOverflowError

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

  2. หากคุณตรวจสอบว่ามีการใช้การสอบถามซ้ำอย่างถูกต้องคุณสามารถเพิ่มขนาดของสแต็กเพื่อให้มีการเรียกใช้จำนวนมากขึ้น ทั้งนี้ขึ้นอยู่กับโปรแกรม Java Virtual Machine (JVM) ติดตั้งขนาดด้ายสแต็คเริ่มต้นอาจเท่ากับทั้ง512KB หรือ 1MB คุณสามารถเพิ่มขนาดเธรดสแต็กได้โดยใช้-Xssแฟล็ก แฟล็กนี้สามารถระบุได้ผ่านการกำหนดค่าของโครงการหรือผ่านบรรทัดคำสั่ง รูปแบบของการ -Xssโต้แย้งคือ: -Xss<size>[g|G|m|M|k|K]


ดูเหมือนว่าจะมีข้อผิดพลาดในบางรุ่นจาวาเมื่อใช้ windows ที่อาร์กิวเมนต์ -Xss จะมีผลเฉพาะกับเธรดใหม่
goerlibe

65

หากคุณมีฟังก์ชั่นเช่น:

int foo()
{
    // more stuff
    foo();
}

จากนั้น foo () จะเรียกมันต่อไปเรื่อย ๆ ลึกขึ้นเรื่อย ๆ และเมื่อพื้นที่ที่ใช้ในการติดตามฟังก์ชั่นที่คุณเติมเต็มอยู่


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

ชื่นบานภาษาใดที่ไม่สามารถใช้งานได้รองรับการเรียกซ้ำแบบหาง
horseyguy

@banister และการใช้งานจาวาสคริปต์บางอย่าง
Pacerier

@horseyguy Scala มีการสนับสนุนการเรียกซ้ำแบบหาง
Ajit K'sagar

สิ่งนี้จะรวบรวมสาระสำคัญของสิ่งที่สามารถสร้างสแต็กล้น ดี
Pixel

24

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

หากสแต็กว่างเปล่าคุณไม่สามารถป๊อปอัปได้หากคุณทำเช่นนั้นคุณจะได้รับข้อผิดพลาดสแต็กอันเดอร์โฟล์ว

หากสแต็คเต็มคุณไม่สามารถผลักดันได้ถ้าคุณทำเช่นนั้นคุณจะได้รับข้อผิดพลาดสแต็คล้น

สแต็คโอเวอร์โฟลว์ปรากฏขึ้นเมื่อคุณจัดสรรสแต็กมากเกินไป ตัวอย่างเช่นในการสอบถามซ้ำดังกล่าว

การใช้งานบางอย่างปรับให้เหมาะสมในการเรียกซ้ำบางอย่าง หางเรียกซ้ำโดยเฉพาะ Tail recursive routines เป็นรูปแบบของชุดคำสั่งที่ recursive call ปรากฏขึ้นเป็นสิ่งสุดท้ายที่สิ่งที่ทำเป็นประจำ การโทรตามปกตินั้นลดลงอย่างรวดเร็ว

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

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


4
มีสิ่งเช่นกองunderflowหรือไม่?
Pacerier

5
สแตก Underflow เป็นไปได้ในการชุมนุม (popping มากกว่าที่คุณผลัก) แม้ว่าในภาษาที่รวบรวมมันจะเป็นไปไม่ได้ใกล้ ฉันไม่แน่ใจคุณอาจสามารถหาการใช้ alloca ของ C ซึ่งเป็น "รองรับ" ขนาดเชิงลบ
คะแนน _ ต่ำกว่า

2
สแต็คโอเวอร์โฟลว์หมายความว่าสแต็คโอเวอร์โฟลว์ โดยปกติจะมีสแต็กหนึ่งรายการในโปรแกรมที่มีตัวแปรในขอบเขต -> ไม่ทุกเธรดมีสแต็กของตัวเองซึ่งมีสแต็กเฟรมสำหรับทุกการเรียกใช้เมธอดที่มีตัวแปรท้องถิ่น ..
Koray Tugay

9

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


1
อุ๊ปส์ไม่เห็นแท็ก Java
Greg

นอกจากนี้จากโปสเตอร์ต้นฉบับที่นี่: ฟังก์ชั่นการทำรังลึกเกินไปในสิ่งที่? ฟังก์ชั่นอื่น ๆ ? และ: ใครจะจัดสรรหน่วยความจำให้กับสแต็กหรือกอง (เนื่องจากคุณรู้ไหมฉันทำสิ่งเหล่านี้อย่างใดอย่างหนึ่งโดยไม่ทราบ)
Ziggy

@ Ziggy: ใช่ถ้าฟังก์ชั่นหนึ่งเรียกใช้ฟังก์ชั่นอื่นซึ่งยังเรียกฟังก์ชั่นอื่นและหลังจากผ่านหลายระดับโปรแกรมของคุณจะมีกองล้น [ดำเนินการต่อ]
Chris Jester-Young

[... ดำเนินการต่อ] ใน Java คุณไม่สามารถจัดสรรหน่วยความจำโดยตรงจากสแต็ก (ใน C คุณสามารถและนี่จะเป็นสิ่งที่ต้องระวัง) ดังนั้นจึงไม่น่าจะเป็นสาเหตุ ใน Java การจัดสรรโดยตรงทั้งหมดมาจากฮีปโดยใช้ "ใหม่"
Chris Jester-Young

@ ChrisJester-Young ไม่เป็นความจริงเลยใช่ไหมถ้าฉันมีตัวแปรในตัว 100 ตัวในหนึ่งวิธีมันทั้งหมดจะไปใน stack โดยไม่มีข้อยกเว้น?
Pacerier

7

อย่างที่คุณพูดคุณต้องแสดงรหัส :-)

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


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

โครงการโอเพนซอร์สของคุณหรือไม่ ถ้าเป็นเช่นนั้นเพียงแค่สร้างบัญชี Sourceforge หรือ GitHub แล้วอัปโหลดรหัสทั้งหมดของคุณที่นั่น :-)
Chris Jester-Young

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


5

StackOverflowErrorคือสแต็กตามที่OutOfMemoryErrorเป็นไปยังกอง

การโทรซ้ำแบบไม่ จำกัด ส่งผลให้พื้นที่สแต็กมีการใช้หมด

ตัวอย่างต่อไปนี้สร้างStackOverflowError:

class  StackOverflowDemo
{
    public static void unboundedRecursiveCall() {
     unboundedRecursiveCall();
    }

    public static void main(String[] args) 
    {
        unboundedRecursiveCall();
    }
}

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


3

นี่คือตัวอย่างของอัลกอริทึมแบบเรียกซ้ำสำหรับการย้อนกลับรายการที่เชื่อมโยงโดยลำพัง บนแล็ปท็อปที่มีสเปคต่อไปนี้ (หน่วยความจำ 4G, Intel Core i5 2.3GHz CPU, 64 บิต Windows 7) ฟังก์ชั่นนี้จะทำงานเป็นข้อผิดพลาด StackOverflow สำหรับรายการที่เชื่อมโยงขนาดใกล้ 10,000

ประเด็นของฉันคือเราควรใช้การเรียกซ้ำอย่างรอบคอบโดยคำนึงถึงระดับของระบบเสมอ บ่อยครั้งที่การเรียกซ้ำสามารถเปลี่ยนเป็นโปรแกรมวนซ้ำได้ (หนึ่งรุ่นซ้ำของอัลกอริทึมเดียวกันได้รับที่ด้านล่างของหน้ามันกลับรายการเชื่อมโยงเดี่ยวขนาด 1 ล้านใน 9 มิลลิวินาที)

    private static LinkedListNode doReverseRecursively(LinkedListNode x, LinkedListNode first){

    LinkedListNode second = first.next;

    first.next = x;

    if(second != null){
        return doReverseRecursively(first, second);
    }else{
        return first;
    }
}

public static LinkedListNode reverseRecursively(LinkedListNode head){
    return doReverseRecursively(null, head);
}

เวอร์ชันซ้ำของอัลกอริทึมเดียวกัน:

    public static LinkedListNode reverseIteratively(LinkedListNode head){
    return doReverseIteratively(null, head);
}   

private static LinkedListNode doReverseIteratively(LinkedListNode x, LinkedListNode first) {

    while (first != null) {
        LinkedListNode second = first.next;
        first.next = x;
        x = first;

        if (second == null) {
            break;
        } else {
            first = second;
        }
    }
    return first;
}


public static LinkedListNode reverseIteratively(LinkedListNode head){
    return doReverseIteratively(null, head);
}

ฉันคิดว่ากับ JVM มันไม่สำคัญว่าแล็ปท็อปของคุณจะเป็นสเป็ค
วิน

3

A StackOverflowErrorเป็นข้อผิดพลาดรันไทม์ใน java

มันจะถูกโยนเมื่อเกินจำนวนหน่วยความจำสแต็กการโทรที่จัดสรรโดย JVM เกิน

กรณีทั่วไปของการStackOverflowErrorถูกโยนคือเมื่อ call stack เกินกว่าเนื่องจากการเรียกซ้ำที่ลึกหรือไม่มีที่สิ้นสุดมากเกินไป

ตัวอย่าง:

public class Factorial {
    public static int factorial(int n){
        if(n == 1){
            return 1;
        }
        else{
            return n * factorial(n-1);
        }
    }

    public static void main(String[] args){
        System.out.println("Main method started");
        int result = Factorial.factorial(-1);
        System.out.println("Factorial ==>"+result);
        System.out.println("Main method ended");
    }
}

การติดตามสแต็ก:

Main method started
Exception in thread "main" java.lang.StackOverflowError
at com.program.stackoverflow.Factorial.factorial(Factorial.java:9)
at com.program.stackoverflow.Factorial.factorial(Factorial.java:9)
at com.program.stackoverflow.Factorial.factorial(Factorial.java:9)

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


0

นี้เป็นกรณีทั่วไปของjava.lang.StackOverflowError... วิธีการที่จะเรียกตัวเองซ้ำที่ไม่มีทางออกในdoubleValue(), floatValue()ฯลฯ

Rational.java

    public class Rational extends Number implements Comparable<Rational> {
        private int num;
        private int denom;

        public Rational(int num, int denom) {
            this.num = num;
            this.denom = denom;
        }

        public int compareTo(Rational r) {
            if ((num / denom) - (r.num / r.denom) > 0) {
                return +1;
            } else if ((num / denom) - (r.num / r.denom) < 0) {
                return -1;
            }
            return 0;
        }

        public Rational add(Rational r) {
            return new Rational(num + r.num, denom + r.denom);
        }

        public Rational sub(Rational r) {
            return new Rational(num - r.num, denom - r.denom);
        }

        public Rational mul(Rational r) {
            return new Rational(num * r.num, denom * r.denom);
        }

        public Rational div(Rational r) {
            return new Rational(num * r.denom, denom * r.num);
        }

        public int gcd(Rational r) {
            int i = 1;
            while (i != 0) {
                i = denom % r.denom;
                denom = r.denom;
                r.denom = i;
            }
            return denom;
        }

        public String toString() {
            String a = num + "/" + denom;
            return a;
        }

        public double doubleValue() {
            return (double) doubleValue();
        }

        public float floatValue() {
            return (float) floatValue();
        }

        public int intValue() {
            return (int) intValue();
        }

        public long longValue() {
            return (long) longValue();
        }
    }

Main.java

    public class Main {

        public static void main(String[] args) {

            Rational a = new Rational(2, 4);
            Rational b = new Rational(2, 6);

            System.out.println(a + " + " + b + " = " + a.add(b));
            System.out.println(a + " - " + b + " = " + a.sub(b));
            System.out.println(a + " * " + b + " = " + a.mul(b));
            System.out.println(a + " / " + b + " = " + a.div(b));

            Rational[] arr = {new Rational(7, 1), new Rational(6, 1),
                    new Rational(5, 1), new Rational(4, 1),
                    new Rational(3, 1), new Rational(2, 1),
                    new Rational(1, 1), new Rational(1, 2),
                    new Rational(1, 3), new Rational(1, 4),
                    new Rational(1, 5), new Rational(1, 6),
                    new Rational(1, 7), new Rational(1, 8),
                    new Rational(1, 9), new Rational(0, 1)};

            selectSort(arr);

            for (int i = 0; i < arr.length - 1; ++i) {
                if (arr[i].compareTo(arr[i + 1]) > 0) {
                    System.exit(1);
                }
            }


            Number n = new Rational(3, 2);

            System.out.println(n.doubleValue());
            System.out.println(n.floatValue());
            System.out.println(n.intValue());
            System.out.println(n.longValue());
        }

        public static <T extends Comparable<? super T>> void selectSort(T[] array) {

            T temp;
            int mini;

            for (int i = 0; i < array.length - 1; ++i) {

                mini = i;

                for (int j = i + 1; j < array.length; ++j) {
                    if (array[j].compareTo(array[mini]) < 0) {
                        mini = j;
                    }
                }

                if (i != mini) {
                    temp = array[i];
                    array[i] = array[mini];
                    array[mini] = temp;
                }
            }
        }
    }

ผลลัพธ์

    2/4 + 2/6 = 4/10
    Exception in thread "main" java.lang.StackOverflowError
    2/4 - 2/6 = 0/-2
        at com.xetrasu.Rational.doubleValue(Rational.java:64)
    2/4 * 2/6 = 4/24
        at com.xetrasu.Rational.doubleValue(Rational.java:64)
    2/4 / 2/6 = 12/8
        at com.xetrasu.Rational.doubleValue(Rational.java:64)
        at com.xetrasu.Rational.doubleValue(Rational.java:64)
        at com.xetrasu.Rational.doubleValue(Rational.java:64)
        at com.xetrasu.Rational.doubleValue(Rational.java:64)
        at com.xetrasu.Rational.doubleValue(Rational.java:64)

นี่คือซอร์สโค้ดของStackOverflowErrorใน OpenJDK 7


0

ใน crunch สถานการณ์ด้านล่างจะทำให้เกิดข้อผิดพลาดมากเกินไปของ Stack

public class Example3 {

public static void main(String[] args) {

    main(new String[1]);

}

}


-1

นี่คือตัวอย่าง

public static void main(String[] args) {
    System.out.println(add5(1));
}

public static int add5(int a) {
    return add5(a) + 5;
}

โดยทั่วไปแล้ว StackOverflowError คือเมื่อคุณพยายามทำบางสิ่งบางอย่างที่มีแนวโน้มว่าจะเรียกตัวเองและไปหาอินฟินิตี้ (หรือจนกว่าจะให้ StackOverflowError)

add5(a) จะเรียกตัวเองแล้วเรียกตัวเองอีกครั้งและอื่น ๆ


-1

คำว่า "stack overrun (ล้น)" มักจะถูกใช้ แต่เรียกชื่อผิด; การโจมตีไม่ล้นกอง แต่บัฟเฟอร์ในกอง

- จากสไลด์บรรยายของศ. ดร. Dieter Gollmann

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