27

このコードがあります:

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」が 8 回表示され、それ以上表示されないのはなぜですか。遭遇しprintln()たときにJavaを呼び出すにはどうすればよいですか?StackOverflowError

4

7 に答える 7

13

ここではキャッチしていますが、その場合はプログラムがクラッシュしてい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

したがって、stackerror6869回(実行ごとに変更)があり、最後の値が出力されます。以前と同じようにを印刷するyと、出力がバッファリングされ、フラッシュされない場合があります。これは、ではないためprintlnです。


アップデート

バッファリングされたを内部System.out.println的に呼び出します。PrintStreamバッファからデータを失うことはありません。データがいっぱいになった後、または明示的にフラッシュを呼び出すと、すべてが出力(この場合はターミナル)に書き込まれます。

このシナリオに戻ると、スタックがいっぱいになる量、キャッチインから実行できた印刷ステートメントの数、doAnything()およびそれらの文字数がバッファーに書き込まれるという内部ダイナミクスに依存します。メインバックでは、最終的に番号が印刷され2ます。

バッファリングされたストリームへのjavadoc参照

于 2013-02-26T07:23:42.910 に答える
5

私の賭けは、catch ブロックで呼び出すことにより、外側のブロックによってキャッチされる別のprintブロックを強制することです。これらの呼び出しの中には、実際に出力ストリームを書き込むための十分なスタックがないものがあります。 StackOverflowError

JLSは次のように述べています。

StackOverflowError は、ネイティブ メソッドの実行または Java 仮想マシン リソースの制限により、メソッド呼び出しによって同期的にスローされる場合と、非同期的にスローされる場合があることに注意してください。

Java SE プラットフォームでは、非同期例外がスローされる前に、少量ではあるが限られた量の実行が許可されます。

上記の遅延は、最適化されたコードがこれらの例外を検出し、Java プログラミング言語のセマンティクスに従いながら例外を処理するのが実用的なポイントでスローできるようにするために許可されています。単純な実装では、各制御転送命令の時点で非同期例外をポーリングする場合があります。プログラムのサイズは有限であるため、これにより、非同期例外を検出する際の合計遅延に制限が設けられます。

于 2013-02-26T07:32:25.940 に答える
3

最初にStackOverFlowErrorが発生すると、 last への呼び出しdoAnything()がキャンセルされ、最後の から catch ブロックに制御が戻されdoAnything()ます。

ただし、スタックはまだ実質的にいっぱいであるため、呼び出しの単純な事実は、スタックに値をプッシュしてから関数を呼び出す必要があるため、System.out.print("y")別の原因になります。StackOverflowErrorprint()

したがって、別のことが再び発生し、戻り値が前の;StackOverflowErrorの catch{} ブロックで返されるようになりました。への単一の呼び出しを実行するために必要なスタック領域の必要性が、からの呼び出しを返すことによって解放される領域の量よりも大きいため、doAnything()別のことが起こります。StackOverflowErrorSystem.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)aの代わりにa が使用されていることに注意してくださいprint(a)。したがって、すべてが正常に実行された場合、各値の後に新しい行を出力する必要がありますa

ただし、実行すると、次の結果が得られます。

X
62066206620662066206620662066206
6190
Y
17
1

これは、catch ブロックの実行が 17 回試行されたことを意味します。これらの catch ブロックの実行のうち、9 つは StackOverflowError を生成する前に何も出力できません。7 は 6190 の値を出力できますが、その後に改行を出力できず、エラーが再び発生します。最後に、6190 の値とその後の改行の両方を出力できるものがあります。したがって、最終的にその catch ブロックが新しい StackOverflowError なしで完了し、コール スタックに正常に戻ることを許可します。

StackOverflowError を扱っているため、これらの数値は単なる例であり、マシン間だけでなく実行間でも大きく異なります。任意の種類の命令を追加または削除するという単純な事実によっても、これらの値が変化するはずです。ただし、ここで見られるパターンは同じままである必要があります。

于 2013-02-27T08:33:27.997 に答える
1

System.out.print("y"); であることは明らかです。in 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
于 2013-02-26T07:47:30.993 に答える
0

いいえ。スタックオーバーフローエラーが発生する回数は未定義です。ただし、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が言ったように、StackOverflowErrorはJVMによって同期的または非同期的にスローされます(つまり、別のスレッド、場合によってはネイティブスレッドによってスローされる可能性があります)。そのため、エラーのスタックトレースを取得できません。いいえ。スレッドがcatch()ブロックにヒットする回数は未定義です。

于 2013-02-26T07:40:34.520 に答える
0

さらに、タイプのオブジェクトは、例外的な条件を表すオブジェクトで Errorはありません。JVM のメモリ不足など、プログラムの実行中に通常は発生しない、プログラム エラーが原因ではない異常な状況を表します。これらは共通のスーパークラスを共有していますが、両方ともスローできることを意味します。ExceptionsErrorsThrowablecatch

于 2013-03-01T07:33:50.070 に答える
-2

スタックオーバーフロー。

例外でのみ印刷していますが、その間、プログラムはオーバーフローに再帰します。

どの時点でこれが発生するかは、個々のシステム、メモリなどによって異なります。

プログラムの目的は何ですか?

于 2013-02-26T07:11:32.123 に答える