12

Javaの例外処理はかなり高価だといつも言われていました。

私はそれがプログラムの始めにそしていつも同じ例外オブジェクトを投げて、新しいものを作成せずに特定の型の例外インスタンスを作成することが良い習慣であるかどうか尋ねています。

例を挙げたいだけです。共通コード

if (!checkSomething(myObject))
   throw new CustomException("your object is invalid");

代替案

static CustomException MYEXP = new CustomException("your object is invalid");

//somewhere else
if (!checkSomething(myObject))
    throw MYEXP;

もちろん、ここではいくつかの仮定をしています。

  1. MyCustomExceptionパラメータはありません
  2. クライアントコードは、良いやり方であるかどうかにかかわらず、例外処理に大きく基づいており、リファクタリングは選択肢ではありません。

質問は以下のとおりです。

  1. これは良い習慣ですか?
  2. これは何らかのJVMメカニズムにダメージを与えますか?
  3. 1が「はい」の場合、パフォーマンスが向上する可能性はありますか? (私はそうは思いませんが、よくわかりません)
  4. 1と3がイエスなら、なぜ実践として後援されないのですか?
  5. 1が「いいえ」の場合、Martin OderskyがScalaの紹介で、これがScalaの動作のしくみであると説明した理由は何ですか? (分28.30にbreakが実装されていると例外がスローされたと語った、聴衆はこれは時間がかかると言い、例外は毎回作成されるわけではないと答えている)Fosdem 2009

私はこれが愚かな質問ではないことを願っています、私はこれについて興味があります。例外処理にかかる実際のコストは取り扱いそして創造ではありません。

編集するFOSDEMプレゼンテーションに関する正確な議論の参照を追加しました

免責事項:私のコードはどれも提案されたようには動作せず、私はこのような例外を管理するつもりはありません。私はただ「what-if」の質問をしています。私が考えたのは、もしそれがScalaで行われるのなら、なぜJavaではないのでしょうか。


  • いずれにしても、どのくらいの頻度で例外をスローすることを期待していますか。そうでなければ...並外れた?もしそうなら、それらを最適化しようとすることのポイントは何ですか? - Michael Myers
  • @MichaelMyersに絶対に同意します。例外のパフォーマンスを最適化する必要があるとは思わないが…ifこれは必要であるthen何かelse間違っている。 - NominSim
  • NominSim:私は仮定2をしています、Michael Myers:パフォーマンスは問題の最も重要なポイントではありません。最も重要なことは、「私は何かを壊していますか」です。 - giampaolo

4 답변


17

いいえ、しないでください。高価な部分は例外を処理していない、それはスタックトレースを生成しています。残念ながら、スタックトレースも便利な部分です。もしあなたが保存された例外を投げるなら、あなたは誤解を招くスタックトレースを渡すでしょう。

Scalaの実装内でこれを行うのが理にかなっている状況があるかもしれません。 (たぶん彼らは再帰的なことをしていて、例外オブジェクトを前もって生成したいのでそれらがメモリを使い果たした場合でも例外を生成することができます。)彼らはまた彼らがしていることについて多くの情報を持っています。それを正しくすることのしかし、JVM言語の実装者による最適化は非常に特殊なケースです。

だからあなたはそうではないでしょう壊れる誤解を招くような情報を提供することは、破損を招くと思わない限り、何でも。それは私にとって大きなリスクのようです。

スタックトレースなしで例外を作成する方法に関するThomas Edingの提案を試してもうまくいくようです。

groovy:000> class MyException extends Exception {
groovy:001>     public Throwable fillInStackTrace() {}}
===> true
groovy:000> e = new MyException()
===> MyException
groovy:000> Arrays.asList(e.stackTrace)
===> []

またチェックアウトJLS

NullPointerException(一種のRuntimeException)です。   メソッドblowUpによってスローされるのは、mainの中のtry文では捕捉されません。   NullPointerExceptionは型の変数に代入できないため   それを吹きました。これによりfinally節が実行され、その後に   テストプログラムの唯一のスレッドであるmainを実行するスレッド   キャッチされていない例外のために終了します。   例外名と単純なバックトレースを表示することに。しかし、   この仕様ではバックトレースは必要ありません。

バックトレースを強制することの問題は、例外が   プログラムのある時点で作成され、後の時点でスローされます。それは   例外がない限り例外でスタックトレースを格納するのは法外に高価です。   実際にスローされます(その場合、トレースは   スタックを巻き戻します。それ故に私達はすべてでバックトレースを強制しません   例外。


  • +1いい答え、ポイントにまっすぐ.. :) - PermGenError
  • しかし、スタックトレースに興味がなく、自分で例外をキャッチするのであれば、少しスピードを上げるための方法かもしれません。またはtsacktraceなしでExceptionを作成する方法はありますか? - MrSmith42
  • @ MrSmith42:私はあなたがスタックトレースなしで例外を作成できるとは思わない。 Rubyのようないくつかの言語は例外ではないものを投げる方法を持っています。 Java-landでは、nullを返すなどの処理が残っています。 Michael Myers'コメントは、例外は例外的であるべきだという良い点を示しました。 - Nathan Hughes
  • @NathanHughes:fillInStackTraceをオーバーライドすると効果がありますか? - Thomas Eding
  • @トーマス:彼らがその公を去ったことに驚きました。答えにそれを加えて、ありがとう。 - Nathan Hughes

3

Q1。これは良い習慣ですか?

私の本にはありません。それは複雑さを増し、診断を妨げます(Q2への私の答えを見てください)。

Q2。これは何らかのJVMメカニズムにダメージを与えますか?

そのような例外オブジェクトからは意味のあるスタックトレースが得られません。

Q3。 1がイエスならば、パフォーマンスの向上はありますか? (私はそうは思いませんが、よくわかりません)

Q4。 1と3がイエスなら、なぜ実践として後援されないのですか?

上記の問題が原因で。

Q5。 1が「いいえ」の場合、Martin OderskyがScalaの紹介で、これがScalaの動作のしくみであると説明した理由は何ですか? (申し訳ありませんが、現時点ではこの肯定の文脈を思い出すことができません)Fosdem 2009

文脈なしで答えるのは難しい。


3

あなたはそれを行うことができますが、例外

  1. 最初のスタックトレースは、その後の使用で混乱を招くだけなので、スタックトレースを含んではいけません。

  2. 抑制された例外を受け入れてはいけません。複数のスレッドが抑制された例外をそれに追加しようとすると、物事は壊れるでしょう。

だからあなたの例外コンストラクタはしなければならない

super(msg, cause, /*enableSuppression*/false, /*writableStackTrace*/false);

見るhttp://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html#Throwable%28java.lang.String,%20java.lang.Throwable,%20boolean,%20boolean%29


今、それは便利ですか?そうです、そうでなければ、なぜこれら2つのブール値フラグがそもそも存在するのでしょうか?:)

複雑な場合には、例外をフロー制御装置として使用することができます。それはより単純でより速いコードを生成するかもしれません。そのような例外は「制御例外」として知られています。

例外が本当にプログラムに例外的に悪い何かを示すことであるなら、それから伝統的な例外を使いなさい。


  • "制御例外"を表示していただきありがとうございます。アイデアはそれほどおかしくありません:) - giampaolo

1

例外は比較的高価で最小限に抑える必要がありますが、「パフォーマンス上の理由から」鈍いことをする必要があるほど高価ではありません。絶対に避けてください。それは完全には真実ではありませんが、あなたは例外がどれくらい遅いかを測定することができます。

long start = System.nanoTime();
int exceptionCount = 0;
for (int i = 0; i < 20000; i++)
    try {
        int j = i / (i & 1);
    } catch (ArithmeticException ae) {
        exceptionCount++;
    }
long time = System.nanoTime() - start;
System.out.printf("Each exception took average of %,d ns%n", time / exceptionCount);

合理的な見積もりであると私が思うものを印刷します。

Each exception took average of 3,064 ns

注:ループ数が増えるにつれて、例外は最適化されます。すなわち10倍の繰り返し

Each exception took average of 327 ns

そして10倍以上

Each exception took average of 35 ns

そして10倍以上

Each exception took average of 5 ns

例外が十分にスローされれば、JITは例外を最適化するのに十分スマートであるように見えます。

リンクされた質問


関連する質問

最近の質問