Java例外パフォーマンスの最適化

2824 ワード

Javaでは、例外オブジェクトの構築に時間がかかるのは、デフォルトでは、例外オブジェクトの作成時に親ThrowablefillInStackTrace()メソッドを呼び出してスタック追跡情報を生成するためです.JDKのソースコードは次のとおりです.
public synchronized Throwable fillInStackTrace() {
        if (stackTrace != null ||
            backtrace != null /* Out of protocol state */ ) {
            fillInStackTrace(0); // native  
            stackTrace = UNASSIGNED_STACK;
        }
        return this;
    }

自分で行ったテストでは、newスタック追跡情報を持つExceptionオブジェクトは、追跡情報を持たないオブジェクトを作成するよりも50倍以上遅い.印刷呼び出しスタックは、エラーが発生したコードの行に正確に位置決めできますが、本当にすべての例外にこれらの情報を生成させる必要があるかどうかを考えてみましょう.
我々はビジネスシステムを開発する過程で、通常、エラー処理ロジックを実現するために異常メカニズムを使用します.これらの異常は通常、2つのクラスに分けることができます.
  • ビジネス例外これらは、システムに問題が発生したことを示すのではなく、ユーザー名パスワードエラー、パラメータエラーなど、通常のビジネスロジック上の必要性を示すカスタムで予知可能な例外です.
  • システム異常は往々にして実行時異常であり、例えばデータベース接続失敗、IO失敗、空ポインタなどであり、このような異常の発生の多くはシステムに問題があることを示し、人工的に位置を調べる必要がある.

  • 実際には、ビジネス異常については、問題を記述する文字列を簡単に知るだけでよく、スタック追跡情報は私たちにとってあまり意味がありません.システム異常に対しては,追跡情報こそ誤りを調べるのに不可欠な参考である.したがって、ビジネス例外を作成するときに呼び出しスタック追跡情報を生成せずにオーバーヘッドを低減し、システム異常が正常に生成されるように制御することができます.
    実際の方法は非常に簡単で、私たちが例外をカスタマイズする場合、親クラスの4つのパラメータを持つ構造方法を書き換えるだけでいいです.この方法はExceptionRuntimeExceptionクラスに存在します.
    protected RuntimeException(String message, Throwable cause,
                                   boolean enableSuppression,
                                   boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    

    これらのパラメータの意味は次のとおりです.
  • message異常の記述情報、すなわちスタック追跡情報を印刷する際に異常クラス名の後に続く記述文字列
  • である.
  • causeは、この異常が発生する親異常、すなわち追跡情報のcaused by
  • をもたらす.
  • enableSuppress異常保留のパラメータについては、ここでは常にfalseに設定すれば
  • になります.
  • writableStackTraceは、スタックトラッキング情報を生成するか否かを示すが、このパラメータをfalseとすれば、異常オブジェクトを構築する際にfillInStackTrace()
  • は呼び出されない.
    たとえば、ビジネス・例外は次のように定義できます.
    public class XXXException extends RuntimeException {
        /**
         *    message,   cause,        ,     
         * @param msg
         */
        public XXXException(String msg) {
            this(msg, false);
        }
    
        /**
         *   message,          
         * @param msg
         * @param recordStackTrace
         */
        public EngineException(String msg, boolean recordStackTrace) {
            super(msg, null, false, recordStackTrace);
        }
    
        /**
         *   message cause,       
         * @param msg
         * @param cause
         */
        public EngineException(String msg, Throwable cause) {
            super(msg, cause, false, true);
        }
    }
    

    すなわち,親クラスの4パラメータの構築手法を用いて異常クラスの挙動を正確に制御する.軽量レベルの例外を作成するには、最初の構造方法を使用します.システムレベルの例外をカプセル化し、スタックトラッキングをログに印刷したい場合は、3番目の構築方法を使用します.
    PS:高同時システムで上記の最適化を行ってこそ明らかな効果が得られる.異常投げが頻繁でなければ明らかな効果はありません.50倍遅くても、実際にはナノ秒級の違いであり、1つのリクエスト処理には微々たるものですから.