finallyブロックは常に実行されますか?


112

最終的にJavaで実行されない可能性がある条件はありますか?ありがとう。


13
これは、ある有名な会社で仕事を得ようとするときに聞かれるかもしれない質問ですか?
トム・ホーティン-タックライン2009年

@ TomHawtin-tackline名前をつけてもいいですか?(神様、私はこの投稿が何歳かわからなかった!)
Hele

6
@Heleゲームをプレゼントしたくありませんが、グーグルできます。
トム・ホーティン-タックライン2013年

短い答え:はい、通常の条件下で。
サメ

回答:


139

Sunチュートリアルから

注:tryまたはcatchコードの実行中にJVMが終了した場合、finallyブロックは実行されない可能性があります。同様に、tryまたはcatchコードを実行しているスレッドが中断または終了すると、アプリケーション全体が継続していても、finallyブロックが実行されない場合があります。

finallyブロックが実行されない他の方法は知りません...


6
@dhiller-「JVMが終了した場合...」に「パワーダウン」が含まれていると確信しています:-p
Jason Coco

2
@Jason Coco:終了(電源喪失時)は終了とまったく同じではありません。後者は多かれ少なかれ組織化されたプロセスであり、前者で最高潮に達します。; p
user359996 2010年

2
私の知る限り、スレッドが中断された場合、それはすぐには停止されません。割り込みを検出してタスクを停止するのはスレッド内のコード次第なので、最後にコードを実行する必要があります。
Bart van Heukelom、2011

2
例外がfinallyブロックでスローされた場合、残りのブロックは実行されないと思います。
Adriaan Koster、2011

よくやった!
eiran 2017

63

System.exitは仮想マシンをシャットダウンします。

現在実行中のJava仮想マシンを終了します。引数はステータスコードとして機能します。慣例により、ゼロ以外のステータスコードは異常終了を示します。

このメソッドは、exitクラスのメソッドを呼び出しますRuntime。このメソッドは通常は戻りません。

    try {
        System.out.println("hello");
        System.exit(0);
    }
    finally {
        System.out.println("bye");
    } // try-finally

"bye"は上記のコードでは出力されません。


また、例外によってJava仮想マシンがシャットダウンされます。
kaissun 2012

3
System.exit(0)の実行中に例外が発生すると、最終的にブロックが実行されます。
ハリル、2015

50

他の人が言ったことを拡大するために、JVMの終了などを引き起こさないものは、最終的にブロックされます。したがって、次のメソッド:

public static int Stupid() {
  try {
    return 0;
  }
  finally {
    return 1;
  }
}

奇妙なことに、コンパイルして1を返します。


2
これは、数週間前の数時間、私を本当に混乱させました。
nickf 2009年

3
finallyブロックから値を返すのは悪い考えです。tryブロックからのみ戻るか、try / finallyブロックの外側から戻ります。ほとんどのIDEはこれを警告でマークします。
Ran Biron、

1
@nickf私はあなたがもう混乱していないことを集めます。1ではなく0が返される理由のメカニズムについて詳しく教えてください。最初に0を保持する関数の戻り値を格納するメモリ(またはレジスタ)は、finallyブロックが実行されるときに上書きされると推測できます。
Yaneeve、2012年

2
C#では、finallyブロックから戻ることはできません。
JMCF125 2013年

3
@RanBironもちろん。彼は、実際にfinallyブロック内に戻ることを推奨していませんでした。彼は、returnステートメントでもブロック内のコードを実行させることを実証しようとしているだけです。
Aquarelle、2015年

15

System.exitに関連して、finallyブロックが実行されない場合がある特定のタイプの壊滅的な障害もあります。JVMが完全にメモリ不足になると、キャッチせずに終了するか、最終的に発生しません。

具体的には、私が愚かに使用しようとしたプロジェクトを覚えています

catch (OutOfMemoryError oome) {
    // do stuff
}

JVMにcatchブロックを実行するためのメモリが残っていないため、これは機能しませんでした。


OutOfMemoryErrorがスローされると、通常は大量のメモリが残ります(GCスラッシングを停止するため)。ただし、繰り返しキャッチすると、GCスラッシングに戻ることは明らかです。
トムホーティン-09年

未チェックの例外をキャッチすべきではないと思いました!
Sergii Shevchyk

1
私はjdk7を使用して自分の側で試しましたが、OutOfMemoryエラーをキャッチしました!
Jaskey 14

10
try { for (;;); } finally { System.err.println("?"); }

その場合、finallyは実行されません(非推奨Thread.stopが呼び出されない限り、または同等のツールインターフェイスを介して呼び出されない限り)。


このページでは、ThreadDeathエラーがスローされ、Thread.stop()が呼び出されたときにスタックが正常に巻き戻されると記載されています。私が見逃しているキャッチはありますか?download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/guide/...
spurserh

間違いはないと思います。たぶん、あなたはそこにない漁獲を想像しています。明示的にを入れるとthrowfinallyブロックは期待どおりに実行されます。try { throw new ThreadDeath(); } finally { System.err.println("?"); }
トム・ホーティン-タックライン

9

このチュートリアルでは、Sunチュートリアルが誤って引用されています。

注:tryまたはcatchコードの実行中にJVMが終了すると、finallyブロック実行されません。同様に、tryまたはcatchコードを実行しているスレッドが中断または強制終了された場合、アプリケーション全体が継続していても、finallyブロック実行されません。

最終的にブロックするために太陽のチュートリアルをよく見ると、「実行されません」とは表示されていませんが、「実行されない可能性があります」これは正しい説明です

注:tryまたはcatchコードの実行中にJVMが終了した場合、finallyブロック実行されない可能性があります。同様に、tryまたはcatchコードを実行しているスレッドが中断または終了すると、アプリケーション全体が継続していても、finallyブロック実行されないことがあります。

この動作の明らかな理由は、system.exit()の呼び出しがランタイムシステムスレッドで処理されるため、jvmをシャットダウンするのに時間がかかる可能性がある一方で、スレッドスケジューラが最終的に実行を要求できるためです。したがって、最終的には常に実行するように設計されていますが、jvmをシャットダウンしている場合、最終的に実行される前にjvmがシャットダウンすることがあります。


6

また、tryブロック内でデッドロック/ライブロックが発生した場合。

これを示すコードは次のとおりです。

public class DeadLocker {
    private static class SampleRunnable implements Runnable {
        private String threadId;
        private Object lock1;
        private Object lock2;

        public SampleRunnable(String threadId, Object lock1, Object lock2) {
            super();
            this.threadId = threadId;
            this.lock1 = lock1;
            this.lock2 = lock2;
        }

        @Override
        public void run() {
            try {
                synchronized (lock1) {
                    System.out.println(threadId + " inside lock1");
                    Thread.sleep(1000);
                    synchronized (lock2) {
                        System.out.println(threadId + " inside lock2");
                    }
                }
            } catch (Exception e) {
            } finally {
                System.out.println("finally");
            }
        }

    }

    public static void main(String[] args) throws Exception {
        Object ob1 = new Object();
        Object ob2 = new Object();
        Thread t1 = new Thread(new SampleRunnable("t1", ob1, ob2));
        Thread t2 = new Thread(new SampleRunnable("t2", ob2, ob1));
        t1.start();
        t2.start();
    }
}

このコードは次の出力を生成します。

t1 inside lock1
t2 inside lock1

そして「最終的に」印刷されることはありません


3
技術的には、tryブロックは存在しないため、finallyブロックは実行される機会がありません。無限ループについても同じことが言えます。
Jeff Mercado

6

tryまたはcatchコードの実行中にJVMが終了すると、finallyブロックが実行されない場合があります。(ソース

通常のシャットダウン-これは、最後のデーモン以外のスレッドが終了したとき、またはRuntime.exit()(ソース) が発生したときに発生します

スレッドが終了すると、JVMは実行中のスレッドのインベントリを実行し、残っているスレッドがデーモンスレッドのみの場合は、通常のシャットダウンを開始します。JVMが停止すると、残りのデーモンスレッドはすべて破棄され、最終的にブロックは実行されません。スタックは、JVMが終了するだけで巻き戻されません。デーモンスレッドの使用は控えめにしてください。クリーンアップを行わなくても、安全に破棄できる処理アクティビティはほとんどありません。特に、あらゆる種類のI / Oを実行する可能性のあるタスクにデーモンスレッドを使用することは危険です。デーモンスレッドは、期限切れのエントリをメモリ内キャッシュから定期的に削除するバックグラウンドスレッドなどの「ハウスキーピング」タスクに最適に保存されます。(ソース

最後のデーモン以外のスレッドの例:

public class TestDaemon {
    private static Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                while (true) {
                    System.out.println("Is alive");
                    Thread.sleep(10);
                    // throw new RuntimeException();
                }
            } catch (Throwable t) {
                t.printStackTrace();
            } finally {
                System.out.println("This will never be executed.");
            }
        }
    };

    public static void main(String[] args) throws InterruptedException {
        Thread daemon = new Thread(runnable);
        daemon.setDaemon(true);
        daemon.start();
        Thread.sleep(100);
        // daemon.stop();
        System.out.println("Last non-daemon thread exits.");
    }
}

出力:

Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Last non-daemon thread exits.
Is alive
Is alive
Is alive
Is alive
Is alive

1

以下の場合、最終的にブロックは実行されません:-

  • ブロックSystem.exit(0)から呼び出されたときtry
  • JVMがメモリ不足になったとき
  • Javaプロセスがタスクマネージャまたはコンソールから強制終了されたとき
  • tryブロックの デッドロック状態
  • 停電によりマシンがシャットダウンしたとき

また、finallyブロックが実行されない場合もあります。


1

最終的にコード実行をブロックするには、2つの方法があり
ます。1. System.exit();を使用します。
2.なんらかの理由で実行制御がブロックを試行するために到達しない場合。
見る:

public class Main
{
  public static void main (String[]args)
  {
    if(true){
        System.out.println("will exceute");
    }else{
        try{
            System.out.println("result = "+5/0);
        }catch(ArithmeticException e){
          System.out.println("will not exceute");
        }finally{
          System.out.println("will not exceute");  
        }
    }
  }
}

0

特にPlayフレームワークに関連して、finallyブロックが実行されないという非常に特殊なケースに遭遇しました。

このコントローラーアクションコードのfinallyブロックが例外の後でのみ呼び出され、呼び出しが実際に成功したときは呼び出されないことに驚いたのです。

try {
    InputStream is = getInputStreamMethod();
    renderBinary(is, "out.zip");
catch (Exception e) {
    e.printStackTrace();
} finally {
    cleanUp();
}

おそらく、renderBinary()が呼び出されたときに、スレッドが終了したか何かです。他のrender()コールでも同じことが起こると思いますが、確認しませんでした。

renderBinary()をtry / catchの後に移動することで問題を解決しました。さらなる調査により、playは@Finallyアノテーションを提供し、コントローラーアクションの実行後に実行されるメソッドを作成することを明らかにしました。ここでの警告は、これはコントローラーでANYアクションが実行された後に呼び出されるため、常に良い選択であるとは限らないということです。


-1
//If ArithmeticException Occur Inner finally would not be executed
class Temp
{
    public static void main(String[] s)
    {
        try
        {
        int x = 10/s.length;
        System.out.println(x);
        try
            {
                int z[] = new int[s.length];
                z[10] = 1000;
            }catch(ArrayIndexOutOfBoundsException e)
            {
                System.out.println(e);
            }
         finally
        {
            System.out.println("Inner finally");
        }
        }
        catch(ArithmeticException e)
        {
            System.out.println(e);
        }
    finally 
    {
        System.out.println("Outer Finally"); 
    }

System.out.println("Remaining Code");   
}
}

インデントを改善し、いくつかの詳細を追加します。
ROMANIA_engineer

1
実行は内側のtryブロックに到達することすらありません。もちろん、内側の最終的には実行されません。
アントン・アリポフ2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.