2017-06-14 8 views
2

wildfly 8.2とJava 1.7を使用しているプロダクション環境では、本当に好奇心が強いです。Javaダイジェストを使用したハッシュ生成が遅くなる

サーバーが2週間以上稼働していると、ログインがパフォーマンスを低下させ始めることがあります。私は問題がどこにあるのかを示す手がかりを探していました。そしていくつかのテストをした後、私は、プレーンテキストに挿入されたパスワードが既に挿入されているパスワードと比較されるように暗号化されているという問題があると結論づけました。

パスワードを暗号化する機能を実行するとほぼ2分かかりますが、サーバーを再起動すると同じ実行に30秒以下かかります。

暗号化では、java.security.MessageDigestを使用してハッシュを生成しています。具体的には、50000回の繰り返しでSHA-256を使用します。なぜこのプロセスが時間とともに遅くなるのか?私は/ dev/randomをランダムに生成するために使用しているので、問題ではないはずです。ここで

は目的球コードです:

protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws UnknownAlgorithmException { 
    MessageDigest digest = getDigest(getAlgorithmName()); 
    if (salt != null) { 
     digest.reset(); 
     digest.update(salt); 
    } 
    byte[] hashed = digest.digest(bytes); 
    int iterations = hashIterations - 1; //already hashed once above 
    //iterate remaining number: 
    for (int i = 0; i < iterations; i++) { 
     digest.reset(); 
     hashed = digest.digest(hashed); 
    } 
    return hashed; 
} 
+0

は、あなたが正確に示されたコードブロックが犯人であることを100%よろしいです?周囲や内部には何もありません。 'getDigest()'や 'getAlgorithmName()'や既存のデータとハッシュを比較するものはありますか? –

+0

メモリリークがないことを確認しましたか?あなたの生活の時間が上がると症状が遅くなる – Pelocho

+0

こんにちは@DominikSandjajaあなたの迅速な答えに感謝します。私のテスト能力は限られているので、コードの断片(認証に使用されている外部ライブラリにあります)にその問題を減らすことができましたが、私はちょうどその行を確信できません。 digest.digest(ハッシュされた)部分。 –

答えて

2

研究のsomedays後、私は私の質問への答えをようやく発見しました。それが他の人にとって有益な場合に備えてここで共有したいと思います。

この問題は、コードキャッシュメモリのために発生しています。私はヒープメモリに焦点を当てていましたが、問題は見られませんでしたが、ヒープ以外のメモリを調べると、ログインプロセスが遅くなり始めたのと同じように、コードキャッシュの半分使用されるメモリ。

このメモリを調査したところ、この領域に大きなドロップがあると、JITコンパイラが機能しなくなることがあります。結論として、これは起こっていたことであり、JITコンパイラをオフにすると、暗号化ループの各反復を各実行ごとに解釈しなければならず、論理的にプロセスが非常に遅くなりました。

ここでは、このトピックで役立つリンクをいくつか残します。

[1] - https://www.quora.com/In-Java-what-exactly-will-the-JVM-interpreter-and-the-JIT-compiler-do-with-the-bytecode

[2] - とにかく、それに答えるために時間がかかる人にhttps://www.atlassian.com/blog/archives/codecache-is-full-compiler-has-been-disabled

おかげ

0

は声明を取り除く:int iterations = hashIterations - 1;とちょうどhashIterationsを使用しています。

最善のケースでは、反復を50000(指定の場合)から49999に減らし、最悪の場合、整数アンダーフローを起こし、最大値の反復をintに増加させます。

hashIterationsがゼロのときに、1を差し引いて最小限のガードを行います。

iterationsの値を記録することによって、デバッグのための計測器についても検討してください。

+0

こんにちは@zaph私の場合iterarionの数は固定されているので、私はあなたが何を意味するかを正しく理解すれば、その場合は起こることができません。とにかくあなたの応答のためのタックス。 –

+0

関数呼び出しは 'hash(byte [] bytes、byte [] salt、int hashIterations)'です。つまり、 'hash'terationsのために' 0' *を渡すことができました。誰も反復回数 '0'で呼び出すことができないことを保証できますか?入力が常に '50000'の場合、その行のコードを持つ理由はありませんが、予期しない結果が生じる可能性があります。コードは、入力があれば、合理的かつ予想通りに動作するように実際に書かれていなければなりません。さもなければ、コードは時間を刻んでいます。 tick-tick-tick -... – zaph

+0

コードの断片が外部ライブラリに属し、反復回数にデフォルト値があり、その関数への呼び出しはライブラリによっても行われています。私は、暗号化されたパスワードから反復回数が取られているため、関数が値0で呼び出されていないことを確信しています(デバッグを行い、数値が常に期待どおりであることを確認してください) –

0

なぜ誰かがこれを閉じますか?多分問題の原因となるものは何もないからです。

通常digest.digestは時間がかかる部分ですが、純粋な計算であり、遅くなる可能性はありません。だから残るのはgetAlgorithmName()getDigest(String)です。前者はおそらく単純なゲッターですが、後者はおそらくMessageDigest.getInstanceを使用してダイジェストを探します。ちょうど推測する:すべてのセキュリティプロバイダとそれらが提供するすべてのものについてルックアップがあり、誰かがこのリストを何とか長くするかもしれない。

このライブラリメソッドは、生産現場でもベンチマークすることができます:メソッドを新しいソースファイルにコピーして、ロギングと定期的に(または手動で)呼び出すコードを追加するだけです。減速が起きると、あなたは何かを比較することになり、あなたのログには詳細なタイミングが見つかるでしょう。

すべて想像の理由が疲れているときは、(あなたが一定であると信じて)iterationsを変えるような非想像のものを試してみてください、など

+1

あなた、 MessageDigest.getInstanceはMessageDigest.getInstance(algorithmName)を使用しています。これは、すべてのセキュリティプロバイダを参照していると言います。私は既にあなたのクラスを複製し、ログを追加することを提案しているのですが、 でした。しかし、問題が再現するまで待たなければならないので、そのログを見ることができます。 –

関連する問題