424

質問:Javaでの例外処理は実際には遅いですか?

Googleの多くの結果と同様に、従来の知恵は、Javaの通常のプログラムフローには例外的なロジックを使用するべきではないと述べています。通常、2つの理由があります。

  1. それは本当に遅い - 通常のコードよりも一桁遅い(与えられた理由は異なる)

そして

  1. 人々はエラーだけが例外的なコードで扱われることを期待するので面倒です。

この質問は#1に関するものです。

例として、このページJavaの例外処理を「非常に遅い」と説明し、そのスローさを例外メッセージ文字列の作成に関連付けます - 「この文字列は、スローされる例外オブジェクトの作成に使用されます。これは高速ではありません。」記事Javaでの効果的な例外処理「この理由は、例外処理のオブジェクト作成の側面によるもので、それによって例外のスローが本質的に遅くなる」と述べています。もう1つの理由は、スタックトレースの生成が遅くなることです。

私のテスト(32ビットLinux上でJava 1.6.0_07、Java HotSpot 10.0を使用)は、例外処理が通常のコードより遅くないことを示しています。私はいくつかのコードを実行するループ内でメソッドを実行しようとしました。メソッドの最後に、ブール値を使用して戻るまたはスロー。このように実際の処理は同じです。 JVMをウォームアップした可能性があると考えて、メソッドを異なる順序で実行し、テスト時間を平均してみました。私のすべてのテストで、スローではないにしても、スローは少なくともリターンと同じくらい速くなっていました(最大3.1%速く)。私は自分のテストが間違っていた可能性について完全にオープンにしていますが、コードサンプル、テスト比較、あるいは実際のところJavaでの例外処理を示す昨年または二年間の結果に関して何かを見たことはありません。スロー。

この道を私が導いたのは、通常の制御ロジックの一部として例外をスローした、私が使用する必要のあるAPIでした。使い方を修正したいのですが、今はできないかもしれません。その代わりに、私は彼らの前向きな考えで彼らを賛美しなければなりませんか?

紙の中ジャストインタイムコンパイルにおける効率的なJava例外処理たとえば、例外がスローされない場合でも、例外ハンドラのみが存在することで、JITコンパイラがコードを適切に最適化できなくなり、処理速度が低下するのを防ぐのに十分であると著者は示唆しています。私はまだこの理論を検証していません。


  • 2)について質問していないことはわかっていますが、プログラムフローに例外を使用することはGOTOを使用することよりも優れていることを認識しておく必要があります。何人かの人々はあなたが話していることを守るが、誰かがあなたが話していることを守るでしょう、しかし、もしあなたがどちらかの期間実装し保守した誰かに尋ねるならば、それらを使用することを決定するのに十分賢いと彼らが考えた人の名前) - Bill K
  • Billは、プログラムフローに例外を使用することがGOTOを使用することよりも優れていると主張しても、プログラムフローに条件文とループを使用することがGOTOを使用するよりも優れていると主張しています。それは赤いニシンです。自分を説明してください。例外は、他の言語でのプログラムフローに使用でき、また効果的に使用されます。例えば、慣用的なPythonコードは例外を定期的に使用します。このようにして例外を使用するコードを作成することはできますが(Javaではなく)、これに本質的に問題があるとは考えていません。 - mmalone
  • 通常の制御フローに例外を使用して@mmaloneを実行するのは、Javaではよくありません。パラダイムの選択はそのように行われました。 Bloch EJ2を読んでください - 彼ははっきりと述べています、引用、(Item 57)exceptions are, as their name implies, to be used only for exceptional conditions; they should never be used for ordinary control flow - その理由について、完全かつ詳細な説明をする。そして彼は男だった書きましたJavaライブラリしたがって、クラスを定義するのは彼です。 API契約/ビルKにこれに同意してください。 - vaxquis
  • @OndraŽižka何らかのフレームワークがこれを実行した場合(例外ではない状態でExceptionsを使用した場合)、それは設計上の欠陥があり、壊れ、言語のExceptionクラス規約に違反します。お粗末なコードを書いているからといって、お粗末なコードを作成することはできません。 - vaxquis
  • ところで、mmaloneのコメントに「本質的に問題があるとは思わない」と言っているにもかかわらず、mmaloneのコメントが非常に多くの支持を得ていることを私は非常に心配しています。 (この場合参照は非常に明白ですが)、「コミュニティの品質保証」というSOがどれほど厳しく強調されているかを強調しているだけなので、人々がそれに同意するのを見るのは悲しいことです。私には時間の経過とともに悪化しました。 - vaxquis

17 답변


313

例外の実装方法によって異なります。最も簡単な方法はsetjmpとlongjmpを使うことです。つまり、CPUのすべてのレジスタがスタックに書き込まれ(すでに時間がかかります)、場合によっては他のデータを作成する必要があります。これはすべてtryステートメントで既に行われています。 throwステートメントは、スタックをアンワインドし、全てのレジスタの値(そしてVM内の可能な他の値)を復元する必要があります。したがって、tryとthrowは同じくらい遅く、それはかなり遅いのですが、例外がスローされなければ、ほとんどの場合tryブロックを終了する時間はまったくかかりません(メソッドが存在する場合はすべて自動的にクリーンアップされるため)。

Sunや他の人たちは、これはおそらく最適とは言えず、もちろんVMはどんどん速くなっていくことを認識していました。例外を実装するもう1つの方法があります。それは、それ自体を速くすることを試みます(実際には、ほとんど何も起こりません - クラスがVMによってロードされるときに必要なことはすべて行われます)。 。どのJVMがこの新しい、より良いテクニックを使っているかわかりません...

...しかし、あなたはJavaで書いているので、あとであなたのコードはある特定のシステム上の1つのJVM上でしか動かないでしょうか?他のプラットフォームや他のJVMバージョン(おそらく他のベンダのバージョン)で実行される可能性があるとしたら、誰もが高速実装を使用すると言っているのでしょうか。速いものは遅いものよりも複雑で、すべてのシステムで簡単には不可能です。あなたは携帯し続けたいですか?それから例外が速いことに頼らないでください。

tryブロックの中であなたがすることにも大きな違いがあります。 tryブロックを開いて、このtryブロック内からメソッドを呼び出さないと、tryブロックは超高速になります。これはJITが実際にスローを単純なgotoのように扱うことができるためです。例外がスローされた場合は、スタック状態を保存する必要も、スタックをアンワインドする必要もありません(キャッチハンドラにジャンプするだけでよい)。しかし、これはあなたが通常することではありません。通常、tryブロックを開いてから、例外をスローする可能性のあるメソッドを呼び出します。メソッド内でtryブロックを使用しただけでも、これはどのようなメソッドになるのでしょうか。それ以外のメソッドは呼び出されません。それは単に数を計算するのでしょうか?では、例外は何が必要ですか。プログラムの流れを調整するためのもっと洗練された方法があります。単純な数学以外のほとんどすべてについて、外部メソッドを呼び出す必要があります。これは既にローカルのtryブロックの利点を破壊します。

次のテストコードを見てください。

public class Test {
    int value;


    public int getValue() {
        return value;
    }

    public void reset() {
        value = 0;
    }

    // Calculates without exception
    public void method1(int i) {
        value = ((value + i) / i) << 1;
        // Will never be true
        if ((i & 0xFFFFFFF) == 1000000000) {
            System.out.println("You'll never see this!");
        }
    }

    // Could in theory throw one, but never will
    public void method2(int i) throws Exception {
        value = ((value + i) / i) << 1;
        // Will never be true
        if ((i & 0xFFFFFFF) == 1000000000) {
            throw new Exception();
        }
    }

    // This one will regularly throw one
    public void method3(int i) throws Exception {
        value = ((value + i) / i) << 1;
        // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
        // an AND operation between two integers. The size of the number plays
        // no role. AND on 32 BIT always ANDs all 32 bits
        if ((i & 0x1) == 1) {
            throw new Exception();
        }
    }

    public static void main(String[] args) {
        int i;
        long l;
        Test t = new Test();

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            t.method1(i);
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method1 took " + l + " ms, result was " + t.getValue()
        );

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            try {
                t.method2(i);
            } catch (Exception e) {
                System.out.println("You'll never see this!");
            }
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method2 took " + l + " ms, result was " + t.getValue()
        );

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            try {
                t.method3(i);
            } catch (Exception e) {
                // Do nothing here, as we will get here
            }
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method3 took " + l + " ms, result was " + t.getValue()
        );
    }
}

結果:

method1 took 972 ms, result was 2
method2 took 1003 ms, result was 2
method3 took 66716 ms, result was 2

tryブロックからの減速は、バックグラウンドプロセスなどの交絡因子を排除するには小さすぎます。しかし、捕獲ブロックはすべてを殺し、それを66倍遅くしました!

私が言ったように、もしあなたがtry / catchを入れて同じメソッド(method3)の中に全部投げても結果はそれほど悪くないでしょう、しかしこれは私が頼りにしない特別なJIT最適化です。この最適化を使用しても、スローはまだかなり遅いです。だから私はあなたがここで何をしようとしているのかわかりませんが、try / catch / throwを使用するより確実にそれを行う方法があります。


  • おかげで、私は例外テストのためにそれほど深くスタックに入っていなかったと思います。これこそまさに私が探していたものです。 - John Ellinwood
  • いい答えですが、私の知る限りでは、System.currentTimeMillis()ではなくSystem.nanoTime()を使用してパフォーマンスを測定するようにしてください。 - Simon Forsberg
  • @ SimonAndr&#233;フォースバーグnanoTime()Java 1.5が必要で、上記のコードを書くために使ったシステムではJava 1.4しか利用できませんでした。また、実際に大きな役割を果たすことはありません。両者の唯一の違いは、一方がナノセカンド、もう一方がミリセカンドです。nanoTimeクロック操作の影響を受けません(テストコードが実行されている瞬間にユーザーまたはシステムプロセスがシステムクロックを変更しない限り、これは無関係です)。一般的にはあなたは正しい、nanoTimeもちろんより良い選択です。 - Mecki
  • あなたのテストは極端な場合です。次のコードを使用すると、パフォーマンスが非常に小さいことがわかります。tryブロックしますが、いいえthrow。きみのthrowテストは例外を投げています時間の50%それは通りますtry。これは明らかに失敗がそうではない状況です。並外れた。これをわずか10%に削減すると、パフォーマンスへの影響が大幅に削減されます。この種のテストの問題点は、例外をまったく使わないようにすることです。例外の使用は、例外的なケース処理のために、テストが示すものよりもはるかに優れたパフォーマンスを発揮します。 - Nate
  • @Glideスローはきれいではありませんreturn。それは体の真ん中のどこかに、おそらく手術の真ん中(それは今のところ50%しか完了していない)の方法さえも残します。catchブロックは上方向に20スタックフレームであるかもしれません(tryblock、method1を呼び出し、method2を呼び出し、method2を呼び出し、method2を呼び出し、method20では、操作の途中で例外がスローされます。スタックは20フレーム上方に巻き戻し、未完成の操作はすべて元に戻し(操作を半分にしてはいけません)、CPUレジスタはクリーンな状態にする必要があります。これはすべて時間がかかります。 - Mecki

228

参考までに、Meckiが行った実験を拡張しました。

method1 took 1733 ms, result was 2
method2 took 1248 ms, result was 2
method3 took 83997 ms, result was 2
method4 took 1692 ms, result was 2
method5 took 60946 ms, result was 2
method6 took 25746 ms, result was 2

最初の3つはMeckiのものと同じです(私のラップトップは明らかに遅いです)。

method4はmethod3と同じですが、new Integer(1)するよりもthrow new Exception()

method 5はmethod 3と似ていますが、new Exception()投げずに。

method6はmethod3と似ていますが、新しい例外を作成するのではなく、事前に作成された例外(インスタンス変数)をスローする点が異なります。

Javaでは、例外をスローするための費用の大部分は、スタックトレースの収集に費やされる時間です。これは、例外オブジェクトが作成されるときに発生します。例外をスローするための実際のコストは、大きいものの、例外を作成するためのコストよりもかなり少なくなります。


  • +1あなたの答えは核心的な問題 - スタックを巻き戻してトレースをたどるのにかかる時間、そして次にエラーを投げることにかかる時間 - に対処します。私はこれを最終的な答えとして選びました。 - Arcane Engineer
  • いいね。 〜70%が例外を作成し、〜30%がそれをスローします。良い情報です。 - chaqke
  • 同様の質問、例外をキャッチし、それを再スローし、そして再度それをキャッチすることには、いくら余分なオーバーヘッドがあるのでしょうか。ありがとう。 - Basil
  • @Basil - あなたは上記の数字からそれを理解できるはずです。 - Hot Licks
  • 標準コードでは、例外の作成とスローはまれに(実行時に)発生します。そうでない場合は、実行時条件が非常に悪いか、デザイン自体が問題になります。どちらの場合も、パフォーマンスは問題になりません... - Jean-Baptiste Yunès

55

AlekseyShipilëvがやりました非常に徹底的な分析彼はさまざまな条件の組み合わせの下でJava例外をベンチマークしています。

  • 新しく作成された例外と事前に作成された例外
  • スタックトレースの有効化と無効化
  • 要求されたスタックトレースと要求されなかったスタックトレース
  • トップレベルでキャッチ/すべてのレベルで再スローvsすべてのレベルでチェーン/ラップ
  • さまざまなレベルのJava呼び出しスタックの深さ
  • インライン最適化なし、極端なインライン化、デフォルト設定なし
  • ユーザー定義フィールドの読み取りと未読み取り

彼はまた、それらをさまざまなレベルのエラー頻度でエラーコードをチェックするパフォーマンスと比較します。

結論(彼の投稿から逐語的に引用)は以下のとおりです。

  1. 本当に例外的な例外は美しく実行されます。設計どおりにそれらを使用し、通常のコードによって処理される圧倒的に多数の例外的でないケースの中で本当に例外的なケースのみを伝達する場合、例外を使用することはパフォーマンス上の利点です。

  2. 例外のパフォーマンスコストには、主に2つの要素があります。スタックトレース構築Exceptionがインスタンス化されているときスタック巻き戻し例外の発生中

  3. スタックトレースの建設費はスタックの深さに比例します例外のインスタンス化の瞬間に。このスローメソッドが呼び出されるスタックの深さを誰が知っているのでしょうか。たとえあなたがスタックトレース生成をオフにしたり例外をキャッシュしたとしても、あなたはパフォーマンスコストのこの部分を取り除くことができるだけです。

  4. スタックの巻き戻しコストは、コンパイルされたコード内で例外ハンドラを近づけることがどれほど幸運であるかによって異なります。深い例外ハンドラの検索を避けるために慎重にコードを構築することは、おそらく私たちが幸運になるのを助けます。

  5. 両方の影響を排除した場合、例外のパフォーマンスコストはローカルブランチのコストとなります。たとえどんなにきれいに聞こえても、それはあなたが通常の制御フローとして例外を使うべきであるという意味ではありません。あなたはコンパイラを最適化するのはやめられない!例外の頻度が高い、本当に例外的な場合にのみそれらを使用してください。償却する実際の例外を発生させる可能性のある不運なコスト

  6. 楽観的な経験則は、10 ^ -4例外の頻度は十分に優れています。もちろん、それは例外自体の重い重み、例外ハンドラでとられる正確なアクションなどに依存します。

結局のところ、例外がスローされない場合はコストがかからないため、例外条件が十分にまれな場合は、例外処理を使用するよりも高速に処理されます。if毎回。全文は読む価値があります。


37

私の答えは、残念ながら、ここに投稿するには長すぎます。それではここで要約してあなたに紹介しましょう。http://www.fuwjax.com/how-slow-are-java-exceptions/粗末な詳細については。

ここでの本当の問題は、「失敗したことがないコード」と比較して「失敗が例外として報告されるのはどれくらい遅いのか」ということではありません。受け入れられた応答はあなたが信じるかもしれないように。そうではなく、「失敗は他の方法で報告された失敗と比べてどれくらい遅いのか」と質問する必要があります。一般に、失敗を報告する他の2つの方法は、センチネル値または結果ラッパーのどちらかです。

Sentinel値は、成功した場合に1つのクラスを返し、失敗した場合に別のクラスを返すという試みです。あなたはほとんど例外を投げるのではなく例外を返すことと考えることができます。これには、成功オブジェクトを持つ共有親クラスが必要です。それから、 "instanceof"チェックを実行して、成功または失敗の情報を取得するために2つのキャストを行います。

型安全性の危険性があるため、Sentinelの値は例外よりも速いのですが、約2倍に過ぎません。今、それは多くのように思えるかもしれませんが、その2倍は実装の違いのコストをカバーするだけです。実際には、このページの他の場所にあるサンプルコードのように、失敗する可能性のあるメソッドは、いくつかの算術演算子よりもはるかに興味深いため、この係数ははるかに低くなります。

結果一方、ラッパーは型安全を犠牲にしません。成功と失敗の情報を単一のクラスにまとめます。そのため、 "instanceof"の代わりに、 "isSuccess()"と、成功オブジェクトと失敗オブジェクトの両方のゲッターを提供します。ただし、結果オブジェクトは約2倍です。もっとゆっくり例外を使用するよりも。毎回新しいラッパーオブジェクトを作成するほうが、例外をスローするよりもはるかにコストがかかることがわかります。

それに加えて、例外は、メソッドが失敗する可能性があることを示す方法として提供されている言語です。どのメソッドが常に(ほとんど)動作することが期待され、どのメソッドが失敗を報告することが期待されるかをAPIだけで判断する方法は他にありません。

例外はセンチネルよりも安全で、結果オブジェクトよりも速く、そしてどちらよりも驚くほどではありません。私は/ catchがif / elseに置き換えられることをお勧めしませんが、ビジネスロジックの中でも、例外が失敗を報告する正しい方法です。

そうは言っても、私が直面したパフォーマンスに大きな影響を与える2つの最も頻繁な方法は、不要なオブジェクトとネストしたループを作成することです。例外を作成するかどうかを選択できる場合は、例外を作成しないでください。常に例外を作成するか、常に別のオブジェクトを作成するかを選択できる場合は、例外を作成してください。


  • レポートなしで失敗をチェックするコントロール実装と比較して、3つの実装の長期的なパフォーマンスをテストすることにしました。このプロセスの失敗率は約4%です。テストの反復は、戦略の1つに対してプロセスを10000回起動します。各戦略は1000回テストされ、最後の900回は統計を生成するために使用されます。以下はnanosでの平均時間です。制御338例外429結果348センチネル345 - Fuwjax
  • ただ楽しみのために、私は例外テストでfillInStackTraceを無効にしました。これが今の時間です。コントロール347例外351結果364センチネル355 - Fuwjax
  • Fuwjax、私が何かを見逃していない限り(そしてあなたのブログ記事ではなくあなたのS / O記事を読むだけだと認める)、あなたの2つのコメントがあなたの記事と矛盾するように思えます。ベンチマークの数値が低いほど良いと思いますが。その場合、fillInStackTraceを有効にして例外を生成すると(これがデフォルトの通常の動作です)、他の2つの手法よりもパフォーマンスが低下します。私は何かが足りないのでしょうか、それともあなたはあなたの投稿を反証するようにコメントしましたか? - Felix GV
  • @Fuwjax - &quot;ロック&ハードプレース&quot;を回避する方法あなたがここに提示する選択は、事前割り当て「成功」を表すオブジェクトです。通常、一般的な失敗の場合にはオブジェクトを事前に割り当てることもできます。その後、追加の詳細を返すというまれなケースでのみ、新しいオブジェクトが作成されます。 (これは、整数の「エラーコード」と同等のオブジェクト指向オブジェクトであり、最後のエラーの詳細を取得するための個別の呼び出し - 何十年も前から存在している手法です。) - ToolmakerSteve
  • @Fuwjaxそれでは、例外をスローしても自分のアカウントでオブジェクトは作成されませんか?その推論が理解できない例外をスローするか結果オブジェクトを返すかにかかわらず、オブジェクトを作成しています。その意味では、結果オブジェクトは例外をスローするより遅くありません。 - Matthias

17

で与えられた答えを拡張します@Meckiそして@incarnate、Java用のスタックトレースの充填なし。

Java 7以降では、Throwable(String message, Throwable cause, boolean enableSuppression,boolean writableStackTrace)。しかしJava6の場合は、この質問に対する私の答え

// This one will regularly throw one
public void method4(int i) throws NoStackTraceThrowable {
    value = ((value + i) / i) << 1;
    // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
    // an AND operation between two integers. The size of the number plays
    // no role. AND on 32 BIT always ANDs all 32 bits
    if ((i & 0x1) == 1) {
        throw new NoStackTraceThrowable();
    }
}

// This one will regularly throw one
public void method5(int i) throws NoStackTraceRuntimeException {
    value = ((value + i) / i) << 1;
    // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
    // an AND operation between two integers. The size of the number plays
    // no role. AND on 32 BIT always ANDs all 32 bits
    if ((i & 0x1) == 1) {
        throw new NoStackTraceRuntimeException();
    }
}

public static void main(String[] args) {
    int i;
    long l;
    Test t = new Test();

    l = System.currentTimeMillis();
    t.reset();
    for (i = 1; i < 100000000; i++) {
        try {
            t.method4(i);
        } catch (NoStackTraceThrowable e) {
            // Do nothing here, as we will get here
        }
    }
    l = System.currentTimeMillis() - l;
    System.out.println( "method4 took " + l + " ms, result was " + t.getValue() );


    l = System.currentTimeMillis();
    t.reset();
    for (i = 1; i < 100000000; i++) {
        try {
            t.method5(i);
        } catch (RuntimeException e) {
            // Do nothing here, as we will get here
        }
    }
    l = System.currentTimeMillis() - l;
    System.out.println( "method5 took " + l + " ms, result was " + t.getValue() );
}

Core i7、8GB RAM上のJava 1.6.0_45での出力:

method1 took 883 ms, result was 2
method2 took 882 ms, result was 2
method3 took 32270 ms, result was 2 // throws Exception
method4 took 8114 ms, result was 2 // throws NoStackTraceThrowable
method5 took 8086 ms, result was 2 // throws NoStackTraceRuntimeException

そのため、値を返すメソッドは、例外をスローするメソッドと比べて高速です。私見、成功と成功の両方に戻り値の型を使用するだけで明確なAPIを設計することはできません。エラーフロースタックトレースなしで例外をスローするメソッドは、通常の例外より4〜5倍高速です。

編集:NoStackTraceThrowable.javaありがとう@Greg

public class NoStackTraceThrowable extends Throwable { 
    public NoStackTraceThrowable() { 
        super("my special throwable", null, false, false);
    }
}


  • おもしろい、ありがとう。欠けているクラス宣言は次のとおりです。public class NoStackTraceThrowable extends Throwable { public NoStackTraceThrowable() { super("my special throwable", null, false, false); } } - Greg

7

これらのトピックが関連しているかどうかわからないが、私はかつて現在のスレッドのスタックトレースに頼る1つのトリックを実装したいと思いました。インスタンス化されたクラスの内部でインスタンス化を引き起こしたメソッドの名前を発見したいと思いました。私は完全にそれをあきらめた)。だから私はその呼び出しを発見したThread.currentThread().getStackTrace()です極めて遅い(ネイティブのためdumpThreads内部的に使用するメソッド)

だからJavaThrowable対応して、ネイティブメソッドがありますfillInStackTrace。キラーだと思うcatch前述のブロックがどういうわけかこのメソッドの実行をトリガーします。

しかし別の話をしましょう…

Scalaでは、いくつかの機能的機能がJVMを使ってコンパイルされています。ControlThrowable拡張するThrowableそしてその上書きfillInStackTrace次のように:

override def fillInStackTrace(): Throwable = this

それで、私は上記のテストを適応させました(サイクル数は10減少します、私のマシンは少し遅いです:):

class ControlException extends ControlThrowable

class T {
  var value = 0

  def reset = {
    value = 0
  }

  def method1(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0xfffffff) == 1000000000) {
      println("You'll never see this!")
    }
  }

  def method2(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0xfffffff) == 1000000000) {
      throw new Exception()
    }
  }

  def method3(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0x1) == 1) {
      throw new Exception()
    }
  }

  def method4(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0x1) == 1) {
      throw new ControlException()
    }
  }
}

class Main {
  var l = System.currentTimeMillis
  val t = new T
  for (i <- 1 to 10000000)
    t.method1(i)
  l = System.currentTimeMillis - l
  println("method1 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method2(i)
  } catch {
    case _ => println("You'll never see this")
  }
  l = System.currentTimeMillis - l
  println("method2 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method4(i)
  } catch {
    case _ => // do nothing
  }
  l = System.currentTimeMillis - l
  println("method4 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method3(i)
  } catch {
    case _ => // do nothing
  }
  l = System.currentTimeMillis - l
  println("method3 took " + l + " ms, result was " + t.value)

}

だから、結果は次のとおりです。

method1 took 146 ms, result was 2
method2 took 159 ms, result was 2
method4 took 1551 ms, result was 2
method3 took 42492 ms, result was 2

あなたが見る、唯一の違いはmethod3そしてmethod4異なる種類の例外を投げるということです。うん、method4まだ遅いですmethod1そしてmethod2しかし、違いははるかに許容範囲です。


7

最初の記事では呼び出しスタックをトラバースしてスタックトレースを作成するという行為は高価な部分であると考えていますが、2番目の記事では述べていませんが、それがオブジェクト作成の最も高価な部分であると思います。ジョンローズは彼が例外をスピードアップするためのさまざまなテクニックを説明している記事。 (例外の事前割り当てと再利用、スタックトレースのない例外など)

しかしそれでも - これは必要な悪、最後の手段にすぎないと考えるべきです。 Johnがこれを行う理由は、(まだ)JVMで利用できない他の言語の機能をエミュレートするためです。制御フローに例外を使用する習慣を身に付けるべきではありません。特にパフォーマンス上の理由ではありません。あなた自身が#2で言及したように、あなたはこのようにあなたのコードの中の重大なバグを隠す危険があります、そしてそれは新しいプログラマーのために保守することがより難しいでしょう。

Javaでのマイクロベンチマークは、特にJIT領域に入ったときには、驚くほどうまくいきません(私が言われました)ので、実際には例外を使用することが「戻る」よりも速いのではないかと思います。たとえば、テストで2〜5スタックフレームのどこかにあると思いますか。今度は、コードがJBossによってデプロイされたJSFコンポーネントによって呼び出されることを想像してください。今、あなたは数ページの長さのスタックトレースを持っているかもしれません。

テストコードを投稿できますか。


6

私はJVM 1.5でいくつかの性能テストをしました、そして、例外を使うことは少なくとも2倍遅くなりました。平均して:例外を除いて3倍以上(3倍)の非常に小さい方法での実行時間。例外をキャッチしなければならなかったごくわずかなループでは、セルフタイムが2倍に増えました。

実稼動コードと同様にマイクロベンチマークでも同じような数字を見ました。

例外は確実にではない頻繁に呼ばれることすべてに使用されます。 1秒間に何千もの例外をスローすると、巨大なボトルネックが発生します。

たとえば、 "Integer.ParseInt(...)"を使用して非常に大きなテキストファイル内のすべての不適切な値を見つける - これは非常に悪い考えです。 (私はこの実用的な方法を見ました殺します製品コードでのパフォーマンス)

例外を使用してユーザーGUIフォームに不正な値を報告します。おそらくパフォーマンスの観点からはそれほど悪くありません。

その良い設計方法であろうとなかろうと、私はルールをたどります:エラーが正常/予想されるものであれば、それから戻り値を使います。異常な場合は、例外を使用してください。例:ユーザー入力を読むと、悪い値が普通です - エラーコードを使用してください。内部のユーティリティ関数に値を渡して、悪い値はコードを呼び出すことによってフィルタリングされるべきです - 例外を使用してください。


  • やり方をいくつか提案しましょう。フォーム内でInteger.valueOf(String)を使用する代わりに数値が必要な場合は、代わりに正規表現照合機能を使用することを検討してください。パターンをプリコンパイルして再利用することで、matcherを安価にすることができます。しかしGUIフォームでは、isValid / validate / checkFieldを持っているか、あなたが持っているものがおそらくより明確になります。また、Java 8にはOptionalモナドがありますので、それらの使用を検討してください。 (答えは9歳ですが、それでも!:p) - Haakon Løtveit

6

しばらく前に、2つのアプローチを使用して文字列を整数に変換する相対的なパフォーマンスをテストするクラスを書きました。(1)Integer.parseInt()を呼び出して例外をキャッチする、または(2)文字列をregexと照合してparseInt()を呼び出す一致が成功した場合のみ私は可能な限り最も効率的な方法で正規表現を使用しました(つまり、ループに入る前にPatternオブジェクトとMatcherオブジェクトを作成しました)。例外からスタックトレースを印刷または保存しませんでした。

1万個の文字列のリストの場合、それらがすべて有効な数値であれば、parseInt()アプローチは正規表現アプローチの4倍の速さでした。しかし、80%の文字列しか有効でなければ、正規表現はparseInt()の2倍の速さでした。また、20%が有効であれば、例外がスローされ、80%の時間がキャッチされたので、正規表現はparseInt()の約20倍の速さでした。

正規表現によるアプローチで有効な文字列が2回処理されることを考えれば、結果には驚きました。1回は一致のため、もう1回はparseInt()のためです。しかし、それを補う以上の例外を投げてキャッチすること。このような状況は現実の世界ではそれほど頻繁には発生しませんが、発生する場合は、必ず例外キャッチ技法を使用しないでください。しかし、ユーザー入力などを検証するだけの場合は、必ずparseInt()アプローチを使用してください。


  • どのJVMを使用しましたか? sun-jdk 6ではまだ遅いのでしょうか。 - Benedikt Waldvogel
  • その答えを提出する前に、私はそれを掘り下げてJDK 1.6u10の下で再度実行しました。それらは私が投稿した結果です。 - Alan Moore
  • これはとても便利です。ありがとう。私の通常のユースケースでは、ユーザー入力を解析する必要があります(以下のようなものを使用します)。Integer.ParseInt()私はそれを期待していますその時間の大半ユーザーの入力は正しいでしょうから、私のユースケースでは、時折例外が発生した場合はそれを回避する方法が適しています。 - markvgti

3

たとえ例外をスローしても遅くないとしても、それでも通常のプログラムフローのために例外をスローするのは悪い考えです。これはGOTOに似た方法で使われています...

私はそれが質問に実際に答えないと思います。例外をスローするという「従来の」知恵は、以前のバージョンのJavaにも当てはまります(&lt; 1.4)。例外を作成するには、VMがスタックトレース全体を作成する必要があります。それ以降、VMの速度を上げるために多くの変更が加えられましたが、これはおそらく改善された1つの分野です。


  • 「通常のプログラムフロー」を定義するとよいでしょう。チェック例外をビジネスプロセスの失敗として使用し、回復不可能なエラーを未チェックの例外として使用することについては多くのことが書かれているので、ある意味でビジネスロジックの失敗は依然として通常のフローと考えることができます。 - Spencer Kormos
  • @Spencer K:その名前のとおり、例外とは例外的な状況が発見されたことを意味します(ファイルが消えた、ネットワークが突然閉じた、など)。これは状況が予想外だったことを意味します。この状況が発生すると予想される場合は、例外を使用しないでください。 - Mecki
  • @メッキ:そうですね。私は最近これについて誰かと議論しました...彼らは検証フレームワークを書いていて、検証失敗の場合に例外を投げていました。これはごく一般的なことなので、これは悪い考えだと思います。メソッドがValidationResultを返すのを見たい場合があります。 - user38051
  • 制御フローの観点では、例外はbreakまたはreturnではないgoto。 - Hot Licks
  • プログラミングのパラダイムはたくさんあります。あなたがそれによって意味するものは何でも、単一の「通常の流れ」になることはできません。基本的に、例外メカニズムは現在のフレームを素早く終了し、特定の時点までスタックを巻き戻すための単なる方法です。 「例外」という言葉は、その「予期しない」性質について何も意味しません。簡単な例:ルーティングの途中で特定の状況が発生したときにWebアプリケーションから404を「スロー」するのは非常に自然なことです。そのロジックが例外で実装されていないのはなぜですか。アンチパターンとは何ですか。 - incarnate

3

HotSpotは、インライン化されている限り、システム生成の例外の例外コードを削除することができます。ただし、明示的に作成された例外とそれ以外の方法で削除されなかった例外は、スタックトレースの作成に多くの時間を費やします。オーバーライドfillInStackTraceこれがパフォーマンスに与える影響を確認する


3

JavaおよびC#での例外パフォーマンスには、まだ望ましいことがたくさんあります。

プログラマとしては、これは、単に実用的なパフォーマンス上の理由から、「例外はめったに発生させないようにする」という規則に従うことを強いられます。

しかし、コンピュータ科学者として、私たちはこの問題のある状態に反抗するべきです。関数をオーサリングする人は、その関数がどれほど頻繁に呼び出されるのか、あるいは成功または失敗の可能性が高いのかどうかわからないことがよくあります。発信者だけがこの情報を持っています。例外を避けようとすると、明確ではないが遅い例外バージョンしかない場合や、高速だが不格好な戻り値エラーがある場合があり、さらに他の場合はその両方が発生するという不明瞭なAPIイデオムが発生します。 。ライブラリの実装者は、2つのバージョンのAPIを記述して保守する必要があります。呼び出し側は、2つのバージョンのうちどちらを使用するかを決定する必要があります。

これはちょっと混乱です。例外のパフォーマンスが優れている場合は、これらの扱いにくい慣用句を使用せずに、例外を使用することができます。

私は本当に戻り値に近いテクニックを使って実装された例外メカニズムを見たいです、それで私たちは戻り値に近いパフォーマンスを持つことができます..これはパフォーマンスに敏感なコードで元に戻すものです。

これは、例外のパフォーマンスとエラーの戻り値のパフォーマンスを比較するサンプルコードです。

パブリッククラスTestIt {

int value;


public int getValue() {
    return value;
}

public void reset() {
    value = 0;
}

public boolean baseline_null(boolean shouldfail, int recurse_depth) {
    if (recurse_depth <= 0) {
        return shouldfail;
    } else {
        return baseline_null(shouldfail,recurse_depth-1);
    }
}

public boolean retval_error(boolean shouldfail, int recurse_depth) {
    if (recurse_depth <= 0) {
        if (shouldfail) {
            return false;
        } else {
            return true;
        }
    } else {
        boolean nested_error = retval_error(shouldfail,recurse_depth-1);
        if (nested_error) {
            return true;
        } else {
            return false;
        }
    }
}

public void exception_error(boolean shouldfail, int recurse_depth) throws Exception {
    if (recurse_depth <= 0) {
        if (shouldfail) {
            throw new Exception();
        }
    } else {
        exception_error(shouldfail,recurse_depth-1);
    }

}

public static void main(String[] args) {
    int i;
    long l;
    TestIt t = new TestIt();
    int failures;

    int ITERATION_COUNT = 100000000;


    // (0) baseline null workload
    for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
        for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
            int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

            failures = 0;
            long start_time = System.currentTimeMillis();
            t.reset();              
            for (i = 1; i < ITERATION_COUNT; i++) {
                boolean shoulderror = (i % EXCEPTION_MOD) == 0;
                t.baseline_null(shoulderror,recurse_depth);
            }
            long elapsed_time = System.currentTimeMillis() - start_time;
            System.out.format("baseline: recurse_depth %s, exception_freqeuncy %s (%s), time elapsed %s ms\n",
                    recurse_depth, exception_freq, failures,elapsed_time);
        }
    }


    // (1) retval_error
    for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
        for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
            int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

            failures = 0;
            long start_time = System.currentTimeMillis();
            t.reset();              
            for (i = 1; i < ITERATION_COUNT; i++) {
                boolean shoulderror = (i % EXCEPTION_MOD) == 0;
                if (!t.retval_error(shoulderror,recurse_depth)) {
                    failures++;
                }
            }
            long elapsed_time = System.currentTimeMillis() - start_time;
            System.out.format("retval_error: recurse_depth %s, exception_freqeuncy %s (%s), time elapsed %s ms\n",
                    recurse_depth, exception_freq, failures,elapsed_time);
        }
    }

    // (2) exception_error
    for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
        for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
            int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

            failures = 0;
            long start_time = System.currentTimeMillis();
            t.reset();              
            for (i = 1; i < ITERATION_COUNT; i++) {
                boolean shoulderror = (i % EXCEPTION_MOD) == 0;
                try {
                    t.exception_error(shoulderror,recurse_depth);
                } catch (Exception e) {
                    failures++;
                }
            }
            long elapsed_time = System.currentTimeMillis() - start_time;
            System.out.format("exception_error: recurse_depth %s, exception_freqeuncy %s (%s), time elapsed %s ms\n",
                    recurse_depth, exception_freq, failures,elapsed_time);              
        }
    }
}

}

そして、これが結果です。

baseline: recurse_depth 2, exception_freqeuncy 0.0 (0), time elapsed 683 ms
baseline: recurse_depth 2, exception_freqeuncy 0.25 (0), time elapsed 790 ms
baseline: recurse_depth 2, exception_freqeuncy 0.5 (0), time elapsed 768 ms
baseline: recurse_depth 2, exception_freqeuncy 0.75 (0), time elapsed 749 ms
baseline: recurse_depth 2, exception_freqeuncy 1.0 (0), time elapsed 731 ms
baseline: recurse_depth 5, exception_freqeuncy 0.0 (0), time elapsed 923 ms
baseline: recurse_depth 5, exception_freqeuncy 0.25 (0), time elapsed 971 ms
baseline: recurse_depth 5, exception_freqeuncy 0.5 (0), time elapsed 982 ms
baseline: recurse_depth 5, exception_freqeuncy 0.75 (0), time elapsed 947 ms
baseline: recurse_depth 5, exception_freqeuncy 1.0 (0), time elapsed 937 ms
baseline: recurse_depth 8, exception_freqeuncy 0.0 (0), time elapsed 1154 ms
baseline: recurse_depth 8, exception_freqeuncy 0.25 (0), time elapsed 1149 ms
baseline: recurse_depth 8, exception_freqeuncy 0.5 (0), time elapsed 1133 ms
baseline: recurse_depth 8, exception_freqeuncy 0.75 (0), time elapsed 1117 ms
baseline: recurse_depth 8, exception_freqeuncy 1.0 (0), time elapsed 1116 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.0 (0), time elapsed 742 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.25 (24999999), time elapsed 743 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.5 (49999999), time elapsed 734 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.75 (99999999), time elapsed 723 ms
retval_error: recurse_depth 2, exception_freqeuncy 1.0 (99999999), time elapsed 728 ms
retval_error: recurse_depth 5, exception_freqeuncy 0.0 (0), time elapsed 920 ms
retval_error: recurse_depth 5, exception_freqeuncy 0.25 (24999999), time elapsed 1121   ms
retval_error: recurse_depth 5, exception_freqeuncy 0.5 (49999999), time elapsed 1037 ms
retval_error: recurse_depth 5, exception_freqeuncy 0.75 (99999999), time elapsed 1141   ms
retval_error: recurse_depth 5, exception_freqeuncy 1.0 (99999999), time elapsed 1130 ms
retval_error: recurse_depth 8, exception_freqeuncy 0.0 (0), time elapsed 1218 ms
retval_error: recurse_depth 8, exception_freqeuncy 0.25 (24999999), time elapsed 1334  ms
retval_error: recurse_depth 8, exception_freqeuncy 0.5 (49999999), time elapsed 1478 ms
retval_error: recurse_depth 8, exception_freqeuncy 0.75 (99999999), time elapsed 1637 ms
retval_error: recurse_depth 8, exception_freqeuncy 1.0 (99999999), time elapsed 1655 ms
exception_error: recurse_depth 2, exception_freqeuncy 0.0 (0), time elapsed 726 ms
exception_error: recurse_depth 2, exception_freqeuncy 0.25 (24999999), time elapsed 17487   ms
exception_error: recurse_depth 2, exception_freqeuncy 0.5 (49999999), time elapsed 33763   ms
exception_error: recurse_depth 2, exception_freqeuncy 0.75 (99999999), time elapsed 67367   ms
exception_error: recurse_depth 2, exception_freqeuncy 1.0 (99999999), time elapsed 66990 ms
exception_error: recurse_depth 5, exception_freqeuncy 0.0 (0), time elapsed 924 ms
exception_error: recurse_depth 5, exception_freqeuncy 0.25 (24999999), time elapsed 23775  ms
exception_error: recurse_depth 5, exception_freqeuncy 0.5 (49999999), time elapsed 46326 ms
exception_error: recurse_depth 5, exception_freqeuncy 0.75 (99999999), time elapsed 91707 ms
exception_error: recurse_depth 5, exception_freqeuncy 1.0 (99999999), time elapsed 91580 ms
exception_error: recurse_depth 8, exception_freqeuncy 0.0 (0), time elapsed 1144 ms
exception_error: recurse_depth 8, exception_freqeuncy 0.25 (24999999), time elapsed 30440 ms
exception_error: recurse_depth 8, exception_freqeuncy 0.5 (49999999), time elapsed 59116   ms
exception_error: recurse_depth 8, exception_freqeuncy 0.75 (99999999), time elapsed 116678 ms
exception_error: recurse_depth 8, exception_freqeuncy 1.0 (99999999), time elapsed 116477 ms

戻り値をチェックして伝播すると、ベースラインヌル呼び出しと比較していくらかのコストが追加され、そのコストは呼び出しの深さに比例します。コールチェーンの深さが8の場合、エラー戻り値チェックバージョンは、戻り値をチェックしなかったベースラインバージョンよりも約27%遅くなりました。

これとは対照的に、例外パフォーマンスは呼び出し深度の関数ではなく、例外頻度の関数です。ただし、例外の頻度が上がるにつれて低下することは、はるかに劇的です。わずか25%のエラー頻度で、コードは24-TIMESより遅く走りました。 100%のエラー頻度では、例外バージョンはほぼ100-TIMES遅くなります。

これは、おそらく私たちの例外実装で間違ったトレードオフをしていることを私に示唆しています。コストのかかるストークウォークを回避することによって、またはそれらをコンパイラでサポートされる戻り値チェックに完全に変換することによって、例外はより速くなる可能性があります。そうするまでは、コードを高速に実行したいときには回避しています。


2

Integer.parseIntを次のメソッドと比較してみてください。例外がスローされるのではなく、解析不可能なデータの場合にはデフォルト値が返されるだけです。

  public static int parseUnsignedInt(String s, int defaultValue) {
    final int strLength = s.length();
    if (strLength == 0)
      return defaultValue;
    int value = 0;
    for (int i=strLength-1; i>=0; i--) {
      int c = s.charAt(i);
      if (c > 47 && c < 58) {
        c -= 48;
        for (int j=strLength-i; j!=1; j--)
          c *= 10;
        value += c;
      } else {
        return defaultValue;
      }
    }
    return value < 0 ? /* übergebener wert > Integer.MAX_VALUE? */ defaultValue : value;
  }

両方の方法を「有効な」データに適用する限り、両方ともほぼ同じ速度で動作します(たとえInteger.parseIntがより複雑なデータを処理することを管理していても)。しかし、無効なデータを解析しようとすると(たとえば "abc"を1000回解析するなど)すぐに、パフォーマンスの違いが重要になります。


0

上記の@Meckiの答えを、method1がブール値と呼び出し側のメソッドのチェックを返すように変更しました。例外を何もなしに置き換えることはできないからです。 2回実行した後も、method1は依然として最速であるか、method2と同じ速さでした。

これがコードのスナップショットです。

// Calculates without exception
public boolean method1(int i) {
    value = ((value + i) / i) << 1;
    // Will never be true
    return ((i & 0xFFFFFFF) == 1000000000);

}
....
   for (i = 1; i < 100000000; i++) {
            if (t.method1(i)) {
                System.out.println("Will never be true!");
            }
    }

そして結果:

実行1

method1 took 841 ms, result was 2
method2 took 841 ms, result was 2
method3 took 85058 ms, result was 2

実行2

method1 took 821 ms, result was 2
method2 took 838 ms, result was 2
method3 took 85929 ms, result was 2


0

例外パフォーマンスに関する素晴らしい投稿は以下のとおりです。

https://shipilev.net/blog/2014/exceptional-performance/

インスタンス化と既存の再利用、スタックトレースありとなし、など

Benchmark                            Mode   Samples         Mean   Mean error  Units

dynamicException                     avgt        25     1901.196       14.572  ns/op
dynamicException_NoStack             avgt        25       67.029        0.212  ns/op
dynamicException_NoStack_UsedData    avgt        25       68.952        0.441  ns/op
dynamicException_NoStack_UsedStack   avgt        25      137.329        1.039  ns/op
dynamicException_UsedData            avgt        25     1900.770        9.359  ns/op
dynamicException_UsedStack           avgt        25    20033.658      118.600  ns/op

plain                                avgt        25        1.259        0.002  ns/op
staticException                      avgt        25        1.510        0.001  ns/op
staticException_NoStack              avgt        25        1.514        0.003  ns/op
staticException_NoStack_UsedData     avgt        25        4.185        0.015  ns/op
staticException_NoStack_UsedStack    avgt        25       19.110        0.051  ns/op
staticException_UsedData             avgt        25        4.159        0.007  ns/op
staticException_UsedStack            avgt        25       25.144        0.186  ns/op

スタックトレースの深さに応じて、

Benchmark        Mode   Samples         Mean   Mean error  Units

exception_0000   avgt        25     1959.068       30.783  ns/op
exception_0001   avgt        25     1945.958       12.104  ns/op
exception_0002   avgt        25     2063.575       47.708  ns/op
exception_0004   avgt        25     2211.882       29.417  ns/op
exception_0008   avgt        25     2472.729       57.336  ns/op
exception_0016   avgt        25     2950.847       29.863  ns/op
exception_0032   avgt        25     4416.548       50.340  ns/op
exception_0064   avgt        25     6845.140       40.114  ns/op
exception_0128   avgt        25    11774.758       54.299  ns/op
exception_0256   avgt        25    21617.526      101.379  ns/op
exception_0512   avgt        25    42780.434      144.594  ns/op
exception_1024   avgt        25    82839.358      291.434  ns/op

その他の詳細(JITのx64アセンブラを含む)については、元のブログ記事を読んでください。

つまり、Hibernate / Spring / etc-EE-shitは、例外(xD)と例外からのアプリケーション制御フローの書き換えのために遅い(と置き換えてください)continure/breakそして戻ってbooleanCのようなメソッド呼び出しからのフラグ)あなたがそれらを投げる頻度に応じて、あなたのアプリケーションのパフォーマンスを10倍-100倍向上させます。


-3

プログラムでデータをチェックすることに対する例外のスピードについての私の意見

多くのクラスは、文字列から値への変換プログラム(スキャナ/パーサ)、有名なライブラリ、そして有名なライブラリも持っていました。

通常形をしている

class Example {
public static Example Parse(String input) throws AnyRuntimeParsigException
...
}

例外名はほんの一例であり、通常はチェックされていない(ランタイム)ので、宣言は私の絵にすぎません

時々二番目の形が存在する:

public static Example Parse(String input, Example defaultValue)

投げない

2番目のものが利用できない場合(またはプログラマがあまりにも少ない数の文書を読んで最初のものだけを使用する場合)は、そのようなコードを正規表現で書いてください。正規表現はかっこいい、政治的に正しい、など。

Xxxxx.regex(".....pattern", src);
if(ImTotallySure)
{
  Example v = Example.Parse(src);
}

このコードを使うことで、プログラマは例外のコストをかけません。しかし、正規表現の非常に高いコストと例外の小さなコストが対比される場合もあります。

私はほとんどいつもそのような文脈で使います

try { parse } catch(ParsingException ) // concrete exception from javadoc
{
}

stacktraceなどを分析せずに、私はあなたの講義の後にかなりの速さを信じます。

例外を恐れてはいけません


-5

例外が通常のリターンよりも遅いのはなぜですか。

スタックトレースを端末に出力したり、ファイルなどに保存したりしない限り、catchブロックは他のコードブロックよりも多くの作業を行いません。それで、「throw new my_cool_error()」がなぜそんなに遅いのか想像できません。

良い質問です。このトピックに関するさらなる情報を楽しみにしています!


  • たとえ実際には使用されなくても、例外はスタックトレースに関する情報を取得する必要があります。 - Jon Skeet

リンクされた質問


関連する質問

最近の質問