2017-06-14 65 views
2

我在使用wildfly 8.2和Java 1.7的生產環境中遇到了一個真正好奇的情況。使用java摘要的哈希生成時間變得更慢

這種情況是,當服務器啓動超過2周時,登錄開始降低性能。我一直在尋找可能表明問題出在哪裏的線索。然後在做了一些測試之後,我得出結論,問題是,以純文本插入的密碼被加密以與已經插入的密碼進行比較。

執行加密密碼的功能需要將近2分鐘,但是當服務器重新啓動時,相同的執行時間少於30秒。

加密使用java.security.MessageDigest生成散列。特別使用SHA-256和50000次迭代。任何想法爲什麼這個過程會隨着時間變慢?我使用/ dev/urandom生成隨機數,所以不應該成爲問題。

這裏是funtion代碼:

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感謝您的快速回答。既然是一個生產環境,我的測試能力是有限的,所以我已經能夠減少問題的代碼片段(是在一個外部庫已被用於身份驗證),但我不能確保完全至極線,我猜是摘要。摘要(散列)部分。 –

回答

2

後的研究唸叨着我終於找到了答案,我的問題。我想在這裏分享它,以防其他人有用。

問題是由於代碼緩存內存造成的。我專注於堆內存,我沒有看到任何問題,但是當我檢查非堆內存時,我發現,就像登錄進程開始放慢一樣,代碼緩存中的一半以上內存使用。

調查這個內存,我發現當這個空間中有大的下降時,可能會發生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的數量是固定的,所以如果我正確理解你的意思,這種情況是不可能發生的。無論如何Thaks您的迴應。 –

+0

函數調用是'hash(byte [] bytes,byte [] salt,int hashIterations)''這意味着''''可以傳入'hashIterations'然後會發生什麼?你能保證沒有人會用'0'的迭代計數嗎?如果輸入始終爲'50000',則沒有理由擁有該行代碼,但可能會導致意外的結果。如果有任何意見,代碼應該寫在合理的預期莊園中。否則,該代碼是一個滴答作響的定時炸彈。 tick-tick-tick -... – zaph

+0

這段代碼屬於一個外部庫,迭代次數有一個默認值,並且該函數的調用也由庫來完成。我確定該函數沒有被調用值0,因爲迭代的次數是從加密的密碼中取出的(同時我也進行了一些調試並確保數字始終是預期的) –

0

爲什麼有人會關閉它?也許是因爲沒有什麼可能會導致問題。

雖然digest.digest通常是耗時的部分,但它是一個純粹的計算,沒有什麼可以減慢它的速度。所以剩下的是getAlgorithmName()getDigest(String)。前者可能是一個簡單的getter,但後者可能使用MessageDigest.getInstance來查找摘要。只是猜測:有一個查詢所有安全提供商和他們提供的所有東西,有人可能會以某種方式延長這個列表。

您可以按基準的這個庫的方法,即使在生產:在新的源文件的方法,只需複製並添加一些記錄和一些代碼定期調用它(或手動,如果你喜歡)。當發生放緩時,您將有一些可以比較的東西,您可以在日誌中找到一些詳細的時間。

當所有可以想象的原因被耗盡,儘量等有所不同iterations非想象的人(你相信這是一個常數)等

+1

您是對的,函數MessageDigest.getInstance使用MessageDigest.getInstance(algorithmName),正如您所說的,它正在通過所有安全提供程序進行查找。我已經做了你正在建議複製類和添加日誌的 。但是我必須等到問題再次複製,以便我能夠看到該日誌。 –