ทำไมวิธีนี้ถึงพิมพ์ 4?


111

ฉันสงสัยว่าจะเกิดอะไรขึ้นเมื่อคุณพยายามจับ StackOverflowError และใช้วิธีการต่อไปนี้:

class RandomNumberGenerator {

    static int cnt = 0;

    public static void main(String[] args) {
        try {
            main(args);
        } catch (StackOverflowError ignore) {
            System.out.println(cnt++);
        }
    }
}

ตอนนี้คำถามของฉัน:

ทำไมวิธีนี้จึงพิมพ์ '4'

ฉันคิดว่าอาจเป็นเพราะSystem.out.println()ต้องการ 3 ส่วนในกองการโทร แต่ฉันไม่รู้ว่าหมายเลข 3 มาจากไหน เมื่อคุณดูซอร์สโค้ด (และ bytecode) ของSystem.out.println()โดยปกติจะนำไปสู่การเรียกใช้เมธอดมากกว่า 3 (ดังนั้น 3 เซ็กเมนต์บน call stack จึงไม่เพียงพอ) หากเป็นเพราะการปรับให้เหมาะสม Hotspot VM ใช้ (วิธีการอินไลน์) ฉันสงสัยว่าผลลัพธ์จะแตกต่างจาก VM อื่นหรือไม่

แก้ไข :

เนื่องจากผลลัพธ์ดูเหมือนจะเฉพาะ JVM สูงฉันจึงได้ผลลัพธ์ 4 โดยใช้
Java (TM) SE Runtime Environment (build 1.6.0_41-b02)
Java HotSpot (TM) 64-Bit Server VM (build 20.14-b01, mixed mode)


คำอธิบายว่าเหตุใดฉันจึงคิดว่าคำถามนี้แตกต่างจากการทำความเข้าใจกับ Java stack :

คำถามของฉันไม่ได้เกี่ยวกับสาเหตุที่มี cnt> 0 (เห็นได้ชัดว่าเป็นเพราะSystem.out.println()ต้องการขนาดสแต็กและโยนอีกอันStackOverflowErrorก่อนที่จะมีการพิมพ์) แต่ทำไมมันถึงมีค่าเฉพาะเป็น 4 ตามลำดับ 0,3,8,55 หรืออย่างอื่นบนอื่น ๆ ระบบ


4
ในพื้นที่ของฉันฉันได้รับ "0" ตามที่คาดไว้
Reddy

2
สิ่งนี้อาจเกี่ยวข้องกับสิ่งที่เกี่ยวกับสถาปัตยกรรมมากมาย ดีกว่าโพสต์ผลลัพธ์ของคุณด้วยเวอร์ชัน jdk สำหรับฉันผลลัพธ์คือ 0 บน jdk 1.7
Lokesh

3
ผมเคย5, 6และ38กับ Java 1.7.0_10
Kon

8
@Elist จะไม่เป็นผลลัพธ์เดียวกันเมื่อคุณใช้เทคนิคที่เกี่ยวข้องกับสถาปัตยกรรมพื้นฐาน;)
m0skit0

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

คำตอบ:


41

ฉันคิดว่าคนอื่น ๆ ทำงานได้ดีในการอธิบายว่าทำไม cnt> 0 แต่ไม่มีรายละเอียดเพียงพอเกี่ยวกับสาเหตุที่ cnt = 4 และเหตุใด cnt จึงแตกต่างกันอย่างมากในการตั้งค่าต่างๆ ฉันจะพยายามเติมช่องว่างตรงนี้

ปล่อย

  • X คือขนาดสแต็กทั้งหมด
  • M คือพื้นที่สแต็กที่ใช้เมื่อเราเข้าสู่หลักในครั้งแรก
  • R คือพื้นที่สแต็กที่เพิ่มขึ้นทุกครั้งที่เราเข้าสู่หลัก
  • P คือพื้นที่สแต็กที่จำเป็นในการเรียกใช้ System.out.println

เมื่อเราเข้าสู่หลักครั้งแรกพื้นที่ที่เหลือคือ XM การเรียกซ้ำแต่ละครั้งจะใช้หน่วยความจำเพิ่มขึ้น R ดังนั้นสำหรับการเรียกซ้ำ 1 ครั้ง (มากกว่าเดิม 1 ครั้ง) การใช้หน่วยความจำคือ M + R สมมติว่า StackOverflowError ถูกโยนหลังจาก C เรียกซ้ำสำเร็จนั่นคือ M + C * R <= X และ M + C * (R + 1)> X ในช่วงเวลาของ StackOverflowError แรกจะมีหน่วยความจำ X - M - C * R เหลืออยู่

เพื่อให้สามารถเรียกใช้System.out.prinlnงานได้เราต้องการพื้นที่ P จำนวนที่เหลือบนสแต็ก ถ้าเป็นเช่นนั้น X - M - C * R> = P ก็จะพิมพ์ 0 หาก P ต้องการพื้นที่เพิ่มเราจะลบเฟรมออกจากสแต็กโดยได้รับหน่วยความจำ R ในราคา cnt ++

เมื่อprintlnใดที่สามารถรันได้ในที่สุด X - M - (C - cnt) * R> = P ดังนั้นถ้า P มีขนาดใหญ่สำหรับระบบใดระบบหนึ่ง cnt จะมีขนาดใหญ่

ลองดูตัวอย่างนี้

ตัวอย่างที่ 1:สมมติว่า

  • X = 100
  • M = 1
  • R = 2
  • P = 1

จากนั้น C = พื้น ((XM) / R) = 49 และ cnt = เพดาน ((P - (X - M - C * R)) / R) = 0

ตัวอย่างที่ 2:สมมติว่า

  • X = 100
  • M = 1
  • R = 5
  • P = 12

จากนั้น C = 19 และ cnt = 2

ตัวอย่างที่ 3:สมมติว่า

  • X = 101
  • M = 1
  • R = 5
  • P = 12

จากนั้น C = 20 และ cnt = 3

ตัวอย่างที่ 4:สมมติว่า

  • X = 101
  • M = 2
  • R = 5
  • P = 12

จากนั้น C = 19 และ cnt = 2

ดังนั้นเราจะเห็นว่าทั้งระบบ (M, R และ P) และขนาดสแต็ก (X) มีผลต่อ cnt

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

แก้ไข

ฉันนำสิ่งที่ฉันพูดถึงcatchกลับไป มันมีบทบาท สมมติว่ามันต้องใช้พื้นที่จำนวน T เพื่อเริ่มต้น cnt จะเริ่มเพิ่มขึ้นเมื่อพื้นที่ที่เหลือมากกว่า T และprintlnทำงานเมื่อพื้นที่ที่เหลือมากกว่า T + P ซึ่งจะเพิ่มขั้นตอนพิเศษในการคำนวณและทำให้การวิเคราะห์ที่เป็นโคลนมากขึ้น

แก้ไข

ในที่สุดฉันก็พบว่ามีเวลาทำการทดลองบางอย่างเพื่อสำรองทฤษฎีของฉัน น่าเสียดายที่ทฤษฎีดูเหมือนจะไม่ตรงกับการทดลอง สิ่งที่เกิดขึ้นจริงแตกต่างกันมาก

การตั้งค่าการทดลอง: เซิร์ฟเวอร์ Ubuntu 12.04 ที่มี java เริ่มต้นและ jdk เริ่มต้น Xss เริ่มต้นที่ 70,000 ที่เพิ่มขึ้นทีละ 1 ไบต์จนถึง 460,000

ดูผลลัพธ์ได้ที่: https://www.google.com/fusiontables/DataSource?docid=1xkJhd4s8biLghe6gZbcfUs3vT5MpS_OnscjWDbM ฉันได้สร้างเวอร์ชันอื่นโดยที่ทุกจุดข้อมูลซ้ำจะถูกลบออก กล่าวอีกนัยหนึ่งจะแสดงเฉพาะจุดที่แตกต่างจากก่อนหน้านี้เท่านั้น ทำให้ง่ายต่อการมองเห็นความผิดปกติ https://www.google.com/fusiontables/DataSource?docid=1XG_SRzrrNasepwZoNHqEAKuZlHiAm9vbEdwfsUA


ขอบคุณสำหรับบทสรุปที่ดีฉันเดาว่าคำถามทั้งหมดมีผลต่อ M, R และ P (เนื่องจาก X สามารถตั้งค่าได้โดย VM-option -Xss)?
flrnb

@flrnb M, R และ P เป็นระบบเฉพาะ คุณไม่สามารถเปลี่ยนสิ่งเหล่านั้นได้อย่างง่ายดาย ฉันคาดว่าพวกเขาจะแตกต่างกันระหว่างบางรุ่นเช่นกัน
John Tseng

แล้วเหตุใดฉันจึงได้ผลลัพธ์ที่แตกต่างจากการเปลี่ยน Xss (aka X) การเปลี่ยน X จาก 100 เป็น 10,000 เนื่องจาก M, R และ P ยังคงเหมือนเดิมไม่ควรส่งผลต่อ cnt ตามสูตรของคุณหรือฉันเข้าใจผิด?
flrnb

@flrnb X เพียงอย่างเดียวจะเปลี่ยน cnt เนื่องจากลักษณะที่ไม่ต่อเนื่องของตัวแปรเหล่านี้ ตัวอย่างที่ 2 และ 3 แตกต่างกันเฉพาะบน X แต่ cnt แตกต่างกัน
John Tseng

1
@JohnTseng ฉันยังถือว่าคำตอบของคุณเข้าใจได้มากที่สุดและสมบูรณ์ที่สุดในตอนนี้ - อย่างไรก็ตามฉันสนใจจริงๆว่าสแต็กมีลักษณะอย่างไรในขณะที่StackOverflowErrorถูกโยนและสิ่งนี้จะส่งผลต่อผลลัพธ์อย่างไร หากมีเพียงการอ้างอิงไปยังสแต็กเฟรมบนฮีป (ตามที่ Jay แนะนำ) ผลลัพธ์ควรจะคาดเดาได้ค่อนข้างชัดเจนสำหรับระบบที่กำหนด
flrnb

20

นี่คือเหยื่อของการโทรซ้ำที่ไม่ดี ตามที่คุณสงสัยว่าทำไมค่าcnt จึงแตกต่างกันนั่นเป็นเพราะขนาดของ stack ขึ้นอยู่กับแพลตฟอร์ม Java SE 6 บน Windows มีขนาดสแต็กเริ่มต้น 320k ใน VM 32 บิตและ 1024k ใน VM 64 บิต คุณสามารถอ่านเพิ่มเติมที่นี่

คุณสามารถเรียกใช้โดยใช้ขนาดสแต็กที่แตกต่างกันและคุณจะเห็นค่าcnt ที่แตกต่างกันก่อนที่สแต็กจะล้น -

java -Xss1024k RandomNumberGenerator

คุณไม่เห็นค่าของcnt ที่ถูกพิมพ์หลายครั้งแม้ว่าค่าจะมากกว่า 1 ในบางครั้งเนื่องจากคำสั่งการพิมพ์ของคุณมีข้อผิดพลาดซึ่งคุณสามารถดีบักเพื่อให้แน่ใจผ่าน Eclipse หรือ IDE อื่น ๆ

คุณสามารถเปลี่ยนรหัสเป็นรหัสต่อไปนี้เพื่อดีบักต่อการดำเนินการคำสั่งหากคุณต้องการ -

static int cnt = 0;

public static void main(String[] args) {                  

    try {     

        main(args);   

    } catch (Throwable ignore) {

        cnt++;

        try { 

            System.out.println(cnt);

        } catch (Throwable t) {   

        }        
    }        
}

อัพเดท:

เนื่องจากสิ่งนี้ได้รับความสนใจมากขึ้นเรามาดูตัวอย่างอื่นเพื่อทำให้สิ่งต่างๆชัดเจนขึ้น -

static int cnt = 0;

public static void overflow(){

    try {     

      overflow();     

    } catch (Throwable t) {

      cnt++;                      

    }

}

public static void main(String[] args) {

    overflow();
    System.out.println(cnt);

}

เราได้สร้างเมธอดอื่นที่ชื่อว่าoverflowเพื่อทำการเรียกซ้ำที่ไม่ถูกต้องและลบคำสั่งprintlnออกจากบล็อก catch เพื่อไม่ให้เกิดข้อผิดพลาดอีกชุดในขณะพยายามพิมพ์ สิ่งนี้ได้ผลตามที่คาดไว้ คุณสามารถลองใส่System.out.println (cnt); คำสั่งหลังจากcnt ++ด้านบนและคอมไพล์ จากนั้นเรียกใช้หลาย ๆ ครั้ง ทั้งนี้ขึ้นอยู่กับแพลตฟอร์มของคุณคุณอาจได้รับค่าที่แตกต่างของCNT

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


13

ลักษณะการทำงานขึ้นอยู่กับขนาดสแต็ก (ซึ่งสามารถตั้งค่าได้ด้วยตนเองโดยใช้Xssขนาดสแต็กเป็นสถาปัตยกรรมเฉพาะจากซอร์สโค้ด JDK 7 :

// ขนาดสแต็กเริ่มต้นบน Windows ถูกกำหนดโดยไฟล์ปฏิบัติการ (java.exe
// มีค่าเริ่มต้น 320K / 1MB [32 บิต / 64 บิต]) ขึ้นอยู่กับเวอร์ชันของ Windows การเปลี่ยน
// ThreadStackSize เป็นไม่ใช่ศูนย์อาจส่งผลกระทบอย่างมากต่อการใช้หน่วยความจำ
// ดูความคิดเห็นใน os_windows.cpp

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

ซ้ำกี่ครั้ง? - มันขึ้นอยู่กับว่าเมื่อใดที่ JVM คิดว่ามันไม่ใช่ stackoverflow อีกต่อไป และขึ้นอยู่กับขนาดสแต็กของแต่ละการเรียกใช้ฟังก์ชัน (หายาก) และXss. ดังที่ได้กล่าวไว้ข้างต้นขนาดรวมเริ่มต้นและขนาดของการเรียกใช้ฟังก์ชันแต่ละครั้ง (ขึ้นอยู่กับขนาดหน้าหน่วยความจำ ฯลฯ ) เป็นแพลตฟอร์มเฉพาะ ดังนั้นพฤติกรรมที่แตกต่างกัน

โทรjavaหา-Xss 4Mฉัน41ด้วย ดังนั้นความสัมพันธ์


4
ฉันไม่เข้าใจว่าเหตุใดขนาดสแต็กจึงส่งผลต่อผลลัพธ์เนื่องจากเกินแล้วเมื่อเราพยายามพิมพ์ค่า cnt ดังนั้นความแตกต่างเพียงอย่างเดียวอาจมาจาก "ขนาดสแต็กของการเรียกใช้ฟังก์ชันแต่ละครั้ง" และฉันไม่เข้าใจว่าทำไมสิ่งนี้จึงแตกต่างกันไประหว่าง 2 เครื่องที่ใช้ JVM เวอร์ชันเดียวกัน
flrnb

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

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

6

ฉันคิดว่าหมายเลขที่แสดงเป็นจำนวนครั้งที่มีการSystem.out.printlnโทรเกิดStackoverflowข้อยกเว้น

อาจขึ้นอยู่กับการใช้งานprintlnและจำนวนการเรียกซ้อนที่ทำในนั้น

เป็นภาพประกอบ:

การmain()โทรทำให้เกิดStackoverflowข้อยกเว้นเมื่อโทร i i-1 โทรจับหลักข้อยกเว้นและโทรซึ่งเรียกเป็นครั้งที่สอง println ได้รับเพิ่มขึ้นถึง 1 i-2 โทรจับหลักในขณะนี้ข้อยกเว้นและการโทร ในเมธอดเรียกว่าทริกเกอร์ข้อยกเว้นที่ 3 ได้รับการเพิ่มขึ้นเป็น 2 สิ่งนี้ดำเนินต่อไปจนกระทั่งสามารถทำการเรียกที่ต้องการทั้งหมดและในที่สุดก็แสดงค่าของ.Stackoverflowcntprintlnprintlncntprintlncnt

printlnนี่คือจากนั้นขึ้นอยู่กับการดำเนินงานที่เกิดขึ้นจริงของ

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


นั่นคือสิ่งที่ฉันหมายถึง "ฉันคิดว่าอาจเป็นเพราะ System.out.println ต้องการ 3 ส่วนใน call stack" - แต่ฉันก็งงว่าทำไมมันถึงเป็นตัวเลขนี้จริงๆและตอนนี้ฉันก็ยิ่งงงว่าทำไมจำนวนจึงแตกต่างกันมาก (virtual) Machines
flrnb

ฉันเห็นด้วยบางส่วน แต่สิ่งที่ฉันไม่เห็นด้วยคือคำสั่ง `ขึ้นอยู่กับการใช้งาน println จริง 'มันเกี่ยวข้องกับขนาดสแต็กในแต่ละ jvm มากกว่าการใช้งาน
Jatin

6
  1. mainrecurses Rกับตัวเองจนล้นสแต็คที่ระดับความลึก
  2. บล็อกจับที่ความลึกของการเรียกซ้ำR-1ถูกเรียกใช้
  3. จับบล็อกที่ recursion ลึกประเมินR-1cnt++
  4. บล็อกการจับที่การR-1โทรเชิงลึกprintlnวางcntค่าเก่าของสแต็ก printlnภายในจะเรียกวิธีการอื่นและใช้ตัวแปรท้องถิ่นและสิ่งต่างๆ กระบวนการทั้งหมดนี้ต้องการพื้นที่สแต็ก
  5. เพราะสแต็คได้แล้วแทะเล็มขีด จำกัด และโทร / การดำเนินการprintlnต้องใช้พื้นที่กองกองล้นใหม่จะถูกเรียกที่ระดับความลึกแทนของความลึกR-1R
  6. ขั้นตอนที่ 2-5 เกิดขึ้นอีกครั้ง R-2แต่ที่ระดับความลึก
  7. ขั้นตอนที่ 2-5 เกิดขึ้นอีกครั้ง R-3แต่ที่ระดับความลึก
  8. ขั้นตอนที่ 2-5 เกิดขึ้นอีกครั้ง R-4แต่ที่ระดับความลึก
  9. ขั้นตอนที่ 2-4 เกิดขึ้นอีกครั้ง R-5แต่ที่ระดับความลึก
  10. มันเกิดขึ้นที่ตอนนี้มีพื้นที่สแต็กเพียงพอสำหรับprintlnการดำเนินการให้เสร็จสมบูรณ์ (โปรดทราบว่านี่เป็นรายละเอียดการใช้งานซึ่งอาจแตกต่างกันไป)
  11. cntถูกโพสต์เพิ่มขึ้นที่ระดับความลึกR-1, R-2, R-3, และสุดท้ายที่R-4 R-5การเพิ่มโพสต์ครั้งที่ห้าส่งคืนสี่ซึ่งเป็นสิ่งที่พิมพ์ออกมา
  12. เมื่อmainเสร็จสมบูรณ์ที่ระดับความลึกR-5สแต็กทั้งหมดจะคลายตัวโดยไม่ต้องมีการเรียกใช้บล็อคจับมากขึ้นและโปรแกรมจะเสร็จสมบูรณ์

1

หลังจากขุดคุ้ยมาสักพักฉันไม่สามารถพูดได้ว่าฉันหาคำตอบได้ แต่ฉันคิดว่ามันค่อนข้างใกล้แล้ว

อันดับแรกเราต้องรู้ว่าเมื่อไรStackOverflowErrorจะถูกโยน ในความเป็นจริงสแต็กสำหรับเธรด java จะเก็บเฟรมซึ่งมีข้อมูลทั้งหมดที่จำเป็นสำหรับการเรียกใช้เมธอดและดำเนินการต่อ ตามข้อกำหนดภาษา Java สำหรับ JAVA 6เมื่อเรียกใช้เมธอด

หากมีหน่วยความจำไม่เพียงพอที่จะสร้างกรอบการเปิดใช้งาน StackOverflowError จะถูกโยนทิ้ง

ประการที่สองเราควรระบุให้ชัดเจนว่าอะไรคือ " มีหน่วยความจำไม่เพียงพอที่จะสร้างกรอบการเปิดใช้งานดังกล่าว " ตามโปรแกรม Java Virtual Machine จำเพาะสำหรับ Java 6 ,

เฟรมอาจถูกจัดสรรเป็นฮีป

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

ทีนี้กลับไปที่คำถาม จากที่กล่าวมาเราสามารถทราบได้ว่าเมื่อมีการดำเนินการเมธอดอาจใช้พื้นที่สแต็กเท่ากัน และการเรียกใช้System.out.println(อาจ) ต้องการการเรียกใช้เมธอด 5 ระดับดังนั้นจึงต้องสร้าง 5 เฟรม จากนั้นเมื่อStackOverflowErrorโยนออกไปจะต้องย้อนกลับ 5 ครั้งเพื่อให้มีพื้นที่สแต็กเพียงพอที่จะจัดเก็บข้อมูลอ้างอิง 5 เฟรม ดังนั้น 4 จึงถูกพิมพ์ออกมา ทำไมไม่ 5? เพราะคุณใช้cnt++. เปลี่ยนเป็น++cntแล้วคุณจะได้รับ 5

และคุณจะสังเกตได้ว่าเมื่อขนาดของสแต็กไปที่ระดับสูงคุณจะได้ 50 ในบางครั้ง นั่นเป็นเพราะต้องนำจำนวนพื้นที่ฮีปที่มีอยู่มาพิจารณา เมื่อขนาดของสแต็กใหญ่เกินไปพื้นที่ฮีปอาจจะหมดก่อนสแต็ก และ (อาจ) ขนาดจริงของสแต็กเฟรมSystem.out.printlnประมาณ 51 เท่าของmainดังนั้นจึงย้อนกลับ 51 ครั้งและพิมพ์ 50


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

@flrnb นั่นเป็นเพราะแพลตฟอร์มที่แตกต่างกันอาจส่งผลต่อขนาดของสแต็กเฟรมและ jre เวอร์ชันอื่นจะส่งผลต่อการนำไปใช้System.out.printหรือกลยุทธ์การดำเนินการตามวิธีการ ตามที่อธิบายไว้ข้างต้นการใช้งาน VM จะส่งผลต่อตำแหน่งที่จะจัดเก็บสแต็กเฟรมจริงด้วย
Jay

0

นี่ไม่ใช่คำตอบของคำถาม แต่ฉันแค่อยากจะเพิ่มบางอย่างในคำถามเดิมที่ฉันเจอและฉันเข้าใจปัญหาได้อย่างไร:

ในปัญหาเดิมจะพบข้อยกเว้นที่เป็นไปได้:

ตัวอย่างเช่นกับ jdk 1.7 จะถูกจับตั้งแต่แรกที่เกิดขึ้น

แต่ในรุ่นก่อนหน้าของ jdk ดูเหมือนว่าข้อยกเว้นจะไม่ถูกจับตั้งแต่แรกที่เกิดขึ้นด้วยเหตุนี้ 4, 50 เป็นต้น

ตอนนี้ถ้าคุณลบ try catch block ดังต่อไปนี้

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

จากนั้นคุณจะเห็นค่าทั้งหมดของcntมดข้อยกเว้นที่ถูกโยน (บน jdk 1.7)

ฉันใช้ netbeans เพื่อดูผลลัพธ์เนื่องจาก cmd จะไม่แสดงผลลัพธ์ทั้งหมดและข้อยกเว้นที่โยนทิ้ง

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