簡體   English   中英

理解Java堆棧

[英]Understanding the Java stack

有這個代碼:

public class Main {
    public static void main(final String[] args) throws Exception {
        System.out.print("1");
        doAnything();
        System.out.println("2");
    }

    private static void doAnything() {
        try {
            doAnything();
        } catch (final Error e) {
            System.out.print("y");
        }
    }
}

還有輸出:

1yyyyyyyy2

為什么它打印“y”八次而不再打印。 遇到StackOverflowError時,Java如何調用println()

在這里你捕獲Error而不是Exception在這種情況下你的程序會崩潰。

如果您嘗試此代碼(修改為添加靜態計數器)

public class StackError {

static int i = 1;

public static void main(final String[] args) throws Exception {
    System.out.print("1");
    doAnything();
    System.out.println("2");
}

private static void doAnything() {
    try {
        i++;
//          System.out.println(i);
        doAnything();
    } catch (Error e) {
        System.out.print("y"+i+"-");

    }
}
}

產量

 1y6869-2

因此,它有stackerror 6869次(不同運行的更改),並打印最后一個值。 如果您只是像之前那樣打印y ,那么輸出可能會緩沖並且不會被刷新,因為它不是println


更新

System.out.println內部調用緩沖的PrintStream 您不會從緩沖區中丟失任何數據,它會在填滿之后或在您明確調用flush時將所有數據寫入輸出(在您的情況下為終端)。

回到這個場景,它取決於堆棧填充多少的內部動態以及doAnything() catch的執行數量,並且這些字符數被寫入緩沖區。 在主背面,它最終印有數字2

javadoc對緩沖流的引用

我敢打賭,通過在catch塊中調用print ,可以強制執行外部塊捕獲的另一個 StackOverflowError 其中一些調用沒有足夠的堆棧來實際寫入輸出流。

JLS說:

請注意,由於本機方法執行或Java虛擬機資源限制,可能會通過方法調用以及異步方式同步拋出StackOverflowError。

Java SE平台允許在拋出異步異常之前進行少量但有限的執行。

上面提到的延遲允許優化代碼在遵循Java編程語言的語義的同時檢測並拋出這些異常。 一個簡單的實現可能會在每個控制傳輸指令的點處輪詢異步異常。 由於程序具有有限大小,因此這提供了檢測異步異常的總延遲的界限。

第一次發生StackOverFlowError時,將取消對最后一個doAnything()的調用,並將控件從最后一個doAnything()返回到catch塊。

但是,因為堆棧仍然幾乎已滿,調用System.out.print("y")的簡單事實將導致另一個StackOverflowError因為需要在堆棧上推送一些值然后調用函數print()

因此,再次發生另一個StackOverflowError ,現在返回上一個doAnything()的catch {}塊返回; 其中會發生另一個StackOverflowError ,因為對System.out.println("y")執行單個調用所需的堆棧空間大於從doAnything()返回調用所釋放的空間量。

只有當堆棧上有足夠的空間來執行對System.out.print("y")的調用時,此進程才會停止並且catch塊將成功完成。 我們可以通過運行以下代碼看到:

public class Principal3b {

    static int a = 0;
    static int i = 0;
    static int j = 0;

    public static void main(String[] args) {
      System.out.println("X");
        doAnything();
      System.out.println("Y");
        System.out.println(i);        
        System.out.println(j);
    }

    private static void doAnything() {

        a++;
        int b = a;

        try {
            doAnything();
        } catch (final Error e) {
            i++;
            System.out.println(a);
            System.out.println(b);
            j++;
        }
    }
}

注意,使用println(a)代替print(a) ; 因此,一個新行的每一個值后打印a ,如果一切運行正常。

但是,當我運行它時,我得到以下結果:

X
62066206620662066206620662066206
6190
Y
17
1

這意味着已經有17次嘗試運行catch塊。 在這些catch塊執行中,9個在生成StackOverflowError之前無法打印任何內容。 7能夠打印6190的值,但是在它們自己再次出現錯誤之前無法打印換行符,最后,有一個能夠打印6190的值和后面的換行符; 因此,最終允許其catch塊在沒有任何新的StackOverflowError的情況下完成,並優雅地返回調用堆棧。

當我們處理StackOverflowError時,這些數字只是一個例子,不僅在機器之間而且在執行之間變化很大,添加或刪除任何類型的指令的簡單事實也應該改變這些值。 但是,這里看到的模式應該保持不變。

有一點很清楚System.out.print(“y”); 在catch中創造了這個難題。 如果我們將代碼更改為

static int n;

public static void main(final String[] args) throws Exception {
    System.out.println("1");
    doAnything();
    System.out.println(n);
}

private static void doAnything() {
    try {
        doAnything();
    } catch (Error e) {
        n++;
    }
}

它打印

1
1

好吧,沒有。 堆棧溢出錯誤被觸發的次數是未定義的。 但是,JVM允許您從StackOverflowError錯誤中恢復並正常繼續執行系統。

通過以下代碼證明:

public class Really {
   public static void main(final String[] args) throws Exception {
     System.out.print("1");
     doAnything();
     System.out.println("2");
   }
   private static void doAnything() {
    try {
           throw new StackOverflowError();
       //doAnything();
    }
    catch(final Error e){
        System.out.print("y");
    }
   }
}

但請注意,正如@Javier所說,JVM同步或異步拋出StackOverflowError (這意味着它可能被另一個線程拋出,可能是本機線程),這就是為什么不可能得到錯誤的堆棧跟蹤的原因。 沒有。 線程中遇到catch()塊的次數是未定義的。

此外, Error類型的對象不是Exceptions對象,它們代表異常條件。 Errors表示不是由程序錯誤引起的異常情況,通常在程序執行期間通常不會發生,例如JVM內存不足。 雖然它們共享一個共同的超類Throwable ,意味着兩者都可以拋出,但它可以放在一個catch但通常不應該被捕獲,因為它們代表罕見的,難以處理的異常條件。

堆棧溢出。

您只是在異常時打印,同時程序會進入溢出狀態。

此時發生的情況取決於個別系統,內存等。

該計划的目的是什么?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM