调整HashMap的大小:未来的危险
最近,我偶然發現了一個錯誤,該錯誤是由于多個線程對java.util.HashMap的使用不當引起的。 該錯誤是抽象泄漏的一個很好的例子。 只有了解數據結構的實現級別詳細信息,才能幫助我解決當前的問題。 因此,我希望分享我所面臨的問題將鼓勵我們的一些讀者熟悉基本數據結構的實現方式。
一天中,通常僅需幾分鐘即可完成的某些分析過程已經運行了數小時,因此,我所面對的癥狀每天都變得丑陋。 作為我們Craft.io的真正信奉者,我們通過自己的監控軟件及時得到通知,并開始調查原因。
我也有幾個來自處理線程的線程轉儲。 他們指出,該代碼只是在堆轉儲中發現的哈希圖中處理條目,看似處于未終止的循環中。 因此,似乎正在分析的數據以某種方式損壞了,其中包含循環引用。
令我驚訝的是,確實如此。 分析的堆內容中的HashMap條目相互引用。 在設計堆分析算法時,我們從未想到這是可能的。 顯然我們錯了。
由于已知HashMap實現不是線程安全的,所以我現在懷疑它與HashMap使用的并發問題有關。 實際上,在java.util.HashMap的設計中隱藏著一個問題。 如您所知, HashMap由存儲區數組組成,每個存儲區均引用一個鏈接的條目列表。 條目依次引用列表中的下一個條目,直到最后一個條目引用null:
我們的分析器遇到的問題是,兩個條目相互引用形成一個封閉的循環。
在Google的幫助下,我發現了最終如何創建這樣的循環引用是多線程環境中的一個問題。 再次提醒您, HashMap在運行時會根據映射中的條目數動態調整大小。 默認情況下, HashMaps使用75%的負載率。 這意味著只要地圖中的條目數超過可用容量的75%,地圖大小就會增加,以避免在地圖元素條目上發生太多沖突。
所以我在這里。 顯然,有多個線程試圖同時調整地圖的大小,從而在某些存儲桶中創建了一個循環。 罪魁禍首最終隱藏在Java HashMap源代碼的以下幾行中:
void transfer(Entry[] newTable, boolean rehash) {... skipped for brevity ...Entry next = e.next;if (rehash) {e.hash = null == e.key ? 0 : hash(e.key);}... skipped for brevity ... }現在,我們的分析端點解決方案非常簡單。 我們只需要保留有關已處理條目的分類帳,而無需對所有條目進行兩次處理就可以了。
我確實相信這是失敗抽象的一個很好的例子。 Java中的HashMaps構建良好,即使您不了解實現細節,也可以很好地為您服務。 直到他們不這樣做。 在這種情況下,對數據結構實現細節的深入了解將為您帶來不同。
翻譯自: https://www.javacodegeeks.com/2016/08/resizing-hashmap-dangers-ahead.html
總結
以上是生活随笔為你收集整理的调整HashMap的大小:未来的危险的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: os是android5.0,拥抱Andr
- 下一篇: Anaconda和canda简介及区别