例外をキャッチするのは費用がかかることを知っています。しかし、例外がスローされない場合でも、Javaでtry-catchブロックを使用するのは高価ですか?
Stack Overflowの質問/回答なぜtryブロックが高価なのですか?が見つかりましたが、それは 。NET 。
try
にはほとんど費用がかかりません。実行時にtry
を設定する作業を行う代わりに、コードのメタデータはコンパイル時に構造化され、例外がスローされると、スタックをさかのぼり、try
ブロックが存在するかどうかを確認する比較的高価な操作を行うようになりましたこの例外をキャッチします。素人の観点からは、try
も自由である可能性があります。実際には例外がスローされるため、コストがかかりますが、数百または数千の例外をスローしない限り、コストに気付くことはありません。
try
には、いくつかの小さなコストが関連付けられています。 Javaは、try
ブロック内のコードに対して、それ以外の方法で行う最適化を実行できません。たとえば、Javaはメソッド内の命令を再配置して、実行速度を向上させますが、Javaは、例外がスローされた場合、メソッドの実行が次のように監視されることを保証する必要もあります:ただし、ソースコードに記述されているステートメントは、最大である行まで順番に実行されます。
try
ブロックでは例外がスローされる可能性があるため(tryブロックの任意の行で!スレッドでstop
を呼び出す(推奨されません)など、一部の例外は非同期にスローされます)それでも同じメソッドでキャッチされ、その後コードが実行され続ける可能性があるため、実行可能な最適化について推論することはより困難であるため、発生する可能性は低くなります。 (誰かがそれらを行うためにコンパイラをプログラムする必要があり、正当性について推論し、保証するなど。それは「例外的」であることを意味する何かにとって大きな痛みになるだろう)しかし、実際には、このようなことに気付かないでしょう。
測定しましょうか?
public abstract class Benchmark {
final String name;
public Benchmark(String name) {
this.name = name;
}
abstract int run(int iterations) throws Throwable;
private BigDecimal time() {
try {
int nextI = 1;
int i;
long duration;
do {
i = nextI;
long start = System.nanoTime();
run(i);
duration = System.nanoTime() - start;
nextI = (i << 1) | 1;
} while (duration < 100000000 && nextI > 0);
return new BigDecimal((duration) * 1000 / i).movePointLeft(3);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() {
return name + "\t" + time() + " ns";
}
public static void main(String[] args) throws Exception {
Benchmark[] benchmarks = {
new Benchmark("try") {
@Override int run(int iterations) throws Throwable {
int x = 0;
for (int i = 0; i < iterations; i++) {
try {
x += i;
} catch (Exception e) {
e.printStackTrace();
}
}
return x;
}
}, new Benchmark("no try") {
@Override int run(int iterations) throws Throwable {
int x = 0;
for (int i = 0; i < iterations; i++) {
x += i;
}
return x;
}
}
};
for (Benchmark bm : benchmarks) {
System.out.println(bm);
}
}
}
私のコンピューターでは、これは次のようなものを出力します:
try 0.598 ns
no try 0.601 ns
少なくともこの些細な例では、tryステートメントはパフォーマンスに測定可能な影響を与えませんでした。より複雑なものは自由に測定してください。
一般的に、コードの実際のパフォーマンスの問題の証拠が得られるまで、言語構造のパフォーマンスコストを心配しないことをお勧めします。またはドナルド・クヌース put it:「時期尚早の最適化はすべての悪の根源です」。
try
/catch
は、パフォーマンスに何らかの影響を与える可能性があります。これは、JVMがいくつかの最適化を実行できないためです。 「効果的なJava」のJoshua Blochは次のように述べています。
•try-catchブロック内にコードを配置すると、最新のJVM実装が実行する可能性のある特定の最適化が禁止されます。
うん、他の人が言ったように、try
ブロックはそれを囲む{}
文字全体のいくつかの最適化を禁止します。特に、オプティマイザーは、ブロック内の任意のポイントで例外が発生する可能性があると想定する必要があるため、ステートメントが実行される保証はありません。
例えば:
try {
int x = a + b * c * d;
other stuff;
}
catch (something) {
....
}
int y = a + b * c * d;
use y somehow;
try
がない場合、x
に割り当てるために計算された値は、「共通部分式」として保存し、y
に割り当てるために再利用できます。ただし、try
により、最初の式が評価されたという保証はないため、式を再計算する必要があります。これは通常、「直線」コードでは大したことではありませんが、ループ内では重要になる可能性があります。
ただし、これはJITCされたコードにのみ適用されることに注意してください。 javacは、最適化のピドリング量だけを実行し、try
ブロックを開始/終了するためのバイトコードインタープリターのコストはゼロです。 (ブロック境界をマークするために生成されたバイトコードはありません。)
そして、bestsssの場合:
public class TryFinally {
public static void main(String[] argv) throws Throwable {
try {
throw new Throwable();
}
finally {
System.out.println("Finally!");
}
}
}
出力:
C:\JavaTools>Java TryFinally
Finally!
Exception in thread "main" Java.lang.Throwable
at TryFinally.main(TryFinally.Java:4)
javap出力:
C:\JavaTools>javap -c TryFinally.class
Compiled from "TryFinally.Java"
public class TryFinally {
public TryFinally();
Code:
0: aload_0
1: invokespecial #1 // Method Java/lang/Object."<init>":()V
4: return
public static void main(Java.lang.String[]) throws Java.lang.Throwable;
Code:
0: new #2 // class Java/lang/Throwable
3: dup
4: invokespecial #3 // Method Java/lang/Throwable."<init>":()V
7: athrow
8: astore_1
9: getstatic #4 // Field Java/lang/System.out:Ljava/io/PrintStream;
12: ldc #5 // String Finally!
14: invokevirtual #6 // Method Java/io/PrintStream.println:(Ljava/lang/String;)V
17: aload_1
18: athrow
Exception table:
from to target type
0 9 8 any
}
「GOTO」はありません。
さらに別のマイクロベンチマーク( source )。
例外の割合に基づいて、try-catchおよびno-try-catchコードバージョンを測定するテストを作成しました。 10%の割合は、テストケースの10%がゼロのケースで除算されたことを意味します。 1つの状況では、try-catchブロックによって処理され、もう1つの状況では条件演算子によって処理されます。これが私の結果表です。
OS: Windows 8 6.2 x64
JVM: Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 23.25-b01
割合|結果(try/if、ns) 0%| 88/90 1%| 89/87 10%| 86/97 90%| 85/83
これは、これらのケースのいずれにも大きな違いはないと言います。
最適化を実行できない理由を理解するには、基礎となるメカニズムを理解することが役立ちます。私が見つけた最も簡潔な例は、次のCマクロに実装されていました。 http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html
#include <stdio.h>
#include <setjmp.h>
#define TRY do{ jmp_buf ex_buf__; switch( setjmp(ex_buf__) ){ case 0: while(1){
#define CATCH(x) break; case x:
#define FINALLY break; } default:
#define ETRY } }while(0)
#define THROW(x) longjmp(ex_buf__, x)
コンパイラは、ジャンプをX、Y、Zにローカライズできるかどうかを判断するのが難しいことが多いため、安全性を保証できない最適化をスキップしますが、実装自体はかなり軽いです。
NullPointErexceptionをキャッチするのは非常に高価だと感じました。 1.2k操作の場合、if(object==null)
と同じ方法で処理したときの時間は200ミリ秒と12ミリ秒でしたが、これはかなり改善されました。