1. ホーム

[解決済み】Exceptionを投げるとき、どの部分が高価なのですか?

2022-04-03 16:28:40

質問

Javaでは、実際にエラーが発生していないときにロジックの一部としてthrow/catchを使用することは、(部分的には)一般的に悪い考えです。例外をスローしてキャッチすることはコストがかかり、ループ内で何度も行うことは、例外をスローしない他の制御構造よりも通常はるかに遅いからです。

質問ですが、そのコストはスロー/キャッチ自体に発生するのでしょうか、それともExceptionオブジェクトの作成時に発生するのでしょうか(実行スタックを含む多くの実行時情報を取得するため)。

つまり、もし私が

Exception e = new Exception();

が、投げてはいけないというのは、投げるコストがほとんどで、コストがかかるのは投げる+捕る処理なのでしょうか?

Try/Catchブロックにコードを入れることでそのコードの実行コストが増えるのかどうかを聞いているのではなく、Exceptionをキャッチすることが高価な部分なのか、Exceptionを作成する(のためのコンストラクタを呼び出す)ことが高価な部分なのかを聞いているのです。

別の言い方をすれば、もし私がExceptionのインスタンスを1つ作り、それを何度も投げてキャッチしたとしたら、投げるたびに新しいExceptionを作るよりもかなり速くなるでしょうか?

解決方法は?

作成する 例外オブジェクトは 必ずしも は、他の通常のオブジェクトを作成するよりもコストがかかります。主なコストはネイティブの fillInStackTrace このメソッドは、コールスタックを走査し、スタックトレースを構築するために必要なすべての情報 (クラス、メソッド名、行番号など) を収集します。

ほとんどの場合 Throwable コンストラクタは暗黙のうちに fillInStackTrace . 例外を作ると遅いというのはここから来ている。しかし、1つの コンストラクタ を作成するために Throwable をスタックトレースなしで表示します。これにより、非常に高速にインスタンス化できるthrowableを作成することができます。軽量な例外を作成するもうひとつの方法は fillInStackTrace .


では、どうでしょう。 投げる は例外ですか?

実は、スローされた例外がどこにあるかによりますが キャッチ .

同じメソッドで (正確には、インライン化によりコンテキストに複数のメソッドが含まれることがあるので、同じコンテキストで) キャッチされた場合、次のようになります。 throw と同じくらい高速でシンプルです。 goto (もちろん、JITコンパイル後です)。

しかし、もし catch ブロックがスタックの深いところにある場合、JVM はスタックフレームを展開する必要があり、これにはかなり時間がかかることがあります。さらに、もし synchronized ブロックやメソッドが含まれる場合は、削除されたスタックフレームが所有するモニタを解放することになるためです。


しかし、幸いなことに、HotSpotのパフォーマンスエンジニアであるAlexey Shipilevの投稿で、すべての側面がすでに完全にカバーされているので、私はこれを行う必要はありません。 リルエクセプションの卓越した性能 .