Java Exception生成コストは高い

Creating an exception in Java is very slow

  • Exceptionがパフォーマンスに影響を及ぼすことについて説明します。
  • 例外を適切に使えばビジネスの明確性を高めるため、各サービスの性質に合わせてCustom Exceptionクラスを作成することになります。実際の開発でも、ControllerやService Layerの入力値を検査する場合、条件を満たすか確認するときif文で検査して、異常入力値であれば、これ以上、下位ロジックを実行せずに終了させる例外クラスを利用することがありますね。Springの場合は、どこでExceptionを投げてもグローバルに対応する@ControllerAdviceが有用です。
  • ビジネスロジックが大きくなるにつれ、状況に応じた例外ケースが増え、Exceptionの発生回数も増えてきましたが、特に問題はなさそうでした。(ControllerAdviceでハンドリングするため)しかし、下記の投稿を発見しました。
  • http://java-performance.info/throwing-an-exception-in-java-is-very-slow/
  • 記事を読むと、例外のstack traceがパフォーマンスに影響を与える大きな要因であることが分かります。例外を作ると、1〜5msを消費するとのことなので、非常に大きなコストになりますね。stack depthによってコストが変わりますが、フレームワーク(ex: Spring)上で実行されるサービスの場合、複雑度に応じてstack depthが途方もなく深くなるため、exception生成コストははるかに高くなります。(10 stack depthで4000 nano second程度ですが、fillInStackTrace()メソッドをOverrideしてこのタスクをスキップすると80 nano second)
  • Filling in the stack trace is slow
at java.lang.Throwable.fillInStackTrace(Throwable.java:-1)
at java.lang.Throwable.fillInStackTrace(Throwable.java:782)
- locked <0x6c> (a sun.misc.CEStreamExhausted)
at java.lang.Throwable.<init>(Throwable.java:250)
at java.lang.Exception.<init>(Exception.java:54)
at java.io.IOException.<init>(IOException.java:47)
at sun.misc.CEStreamExhausted.<init>(CEStreamExhausted.java:30)
at sun.misc.BASE64Decoder.decodeAtom(BASE64Decoder.java:117)
at sun.misc.CharacterDecoder.decodeBuffer(CharacterDecoder.java:163)
at sun.misc.CharacterDecoder.decodeBuffer(CharacterDecoder.java:194)

解決策

  1. Overriding fillInStackTrace method
  • exception stack traceはThrowable.fillInStackTraceメソッドによって生成されるので、これをtraceを持たないようにOverrideしておきます。
@Override 
public synchronized Throwable fillInStackTrace() {
  return this;
}
  1. Caching an exception
  • stack traceを持たないようにOverridingしておいたExceptionであれば、static finalで宣言して、一種の定数値の形態で例外をキャッシュしておきましょう。毎回newで生成するよりも効率的です。
public class CustomException extends RuntimeException {
    public static final CustomException INVALID_NICKNAME = new CustomException(ResponseType.INVALID_NICKNAME);
    public static final CustomException INVALID_PARAMETER = new CustomException(ResponseType.INVALID_PARAMETER);
    public static final CustomException INVALID_TOKEN = new CustomException(ResponseType.INVALID_TOKEN);
    //省略
}
  • Exceptionクラスに例外状況に対する適切な応答メッセージやコードを盛り込むようにした後、例外の発生状況で、newキーワードなしでthrowします。
if (StringUtils.isBlank(parameter)) {
    throw WebtoonCoreException.INVALID_PARAMETER;
}
  • このように、投げた例外をフレームワークでハンドリングする領域で処理したり、呼び出しクラスに渡して処理することができます。

exceptionとともにloggingも適切に使用するとパフォーマンスの面で役立つことでしょう。

TOAST Meetup 編集部

TOASTの技術ナレッジやお得なイベント情報を発信していきます
pagetop