2011-04-30 48 views
0

我在我的應用程序中有一個散列表。該地圖處於單身狀態,並且使用同步方法保護對更新或讀取的訪問權限。散列表放置性能問題

當使用大量(20000+)併發線程進行測試時,會出現我的問題。當線程正在使用put()即時獲取OutOfMemory異常寫入映射。

讀取操作很好(我可以模擬100多個線程),沒有任何問題。

任何關於如何使我的hashmap更高性能寫入的建議?這也可能是我在存儲器中存儲這麼多數據的方法的限制嗎?

+1

使用數據庫,也許? – 2011-04-30 23:22:36

+0

是的,像這樣的一大組數據應該(部分)存儲在磁盤備份的散列映射中,這是一個數據庫。 – 2011-04-30 23:45:28

+0

數據庫不是這個特定應用 – cduggan 2011-05-01 11:54:11

回答

4

由於線程數量的限制,我懷疑您的PermGen內存不足。你的OutOfMemoryError異常應該告訴你它是堆還是PermGen。

Java中的每個線程都使用大約256-512千字節的堆棧,它是從PermGen分配的。因此,超過默認PermGen大小(通常爲64-256 MB)的20,000個線程* 256 KB = 5 GB。

您應該將線程數限制在少於幾百個。看看Java 5/6的併發包,特別是ThreadPoolExecutor

+0

感謝您提供良好的信息,我如何查看它是否出於PermGen?當拋出OOM異常時,它只會給出一個下面的異常「OutOfMemoryError:無法創建新的本地線程」以及堆棧跟蹤。當我使用探查器時,生成的日誌顯示異常拋出「java.lang.OutOfMemoryError:請求160008字節的Chunk :: new。Out of swap space?」。在這個日誌中,perm gen-total = 11288k,used = 4498k。我認爲使用探查器導致不同的內存使用情況,所以我不能相信它。我嘗試使用-XX:HeapDumpPath arg在OOM發生時創建堆轉儲,但不是。 – cduggan 2011-05-01 09:34:36

+0

如果讀取操作允許100000個線程,那麼這是一個PermGen問題? – cduggan 2011-05-01 11:55:26

+0

'無法創建新的本地線程',所以它出現了Java,無論出於何種原因,都無法創建新的線程。所以看起來你有太多的線程。 – 2011-05-01 15:22:42

1

如果您使用JDK1.5 +,ConcurrentHashMap是一個不錯的選擇。這很有效。

參見:What's the difference between ConcurrentHashMap and Collections.synchronizedMap(Map)?

另外,我覺得put()可能會導致新的存儲器中的地圖和更費時分配,但不get()。所以更多的線程將被阻止在put()

此外,優化您的關鍵類的hashCode()方法。這很重要,因爲散列碼計算在您的案例中是密集型操作。如果密鑰對象是不可變的,則只需計算一次散列代碼並將其保存爲成員並直接在hashCode()中返回。

+0

有關優化哈希碼功能的選項,如果即時通訊使用Integer作爲密鑰,我不相信它的哈希碼會比我能寫的更好嗎?我沒有使用自定義對象的散列圖 – cduggan 2011-05-01 09:26:19

+0

在我的地圖中,我還重用鍵來輸入新的值,當滿足某些條件。這會增加內存問題嗎? – cduggan 2011-05-01 16:22:59

-2

OutOfMemoryError可能是由大量的對象存儲而不是大量的線程引起的,而OOME不是性能問題。

順便說一句,您可以使用ConcurrentHashMap進行快速併發讀取和寫入,並且不使用一個全局鎖。

1

你試過了一個ConcurrentHashMap嗎?在正確的編碼環境下,您不需要任何同步。內部有多個分條鎖以減少爭用,以及許多好的複合原子操作,如putIfAbsent,可能允許您完全刪除外部鎖。

至於內存,我懷疑你真的在JVM中存儲了很多。使用像visualvm這樣的監視工具來檢查它,或者爲JVM分配添加更多的內存。考慮像EHCache這樣的緩存,它會自動溢出到磁盤,並在內部使用ConcurrentHashMap,並有各種不同的邊界選項

+0

歡呼,與visualvm我可以確定它確定太多的線程。 – cduggan 2011-05-03 18:37:14

1

聽起來像你的問題是內存,而不是性能。

嘗試使用相同的哈希碼將最近最少訪問的鍵和值寫入文件 ,並從內存中清除它們。

如果存儲哈希碼的文件被尋址,則將最近使用的哈希碼的密鑰和銷售額寫入文件並從存儲器中清除,然後將期望存儲的讀取文件讀取到存儲器。

考慮多層次的hashmaps(每個都有不同的鍵)以提高性能。

1

如果您想保留當前的實現,您可能還需要考慮通過更改傳遞給Java的-Xms和-Xmx參數來更改分配給應用程序的內存量。還有許多其他參數。無論使用何種實現,都可能需要這樣做。

1

您可以使用ConcurrentHashMap而不是它,它比常規地圖更有優勢。 我不確定您是否使用Java5,因爲它僅適用於版本5。

此外,我會說,再次想一想你的邏輯,你是否真的需要讀取操作的同步。如果不是的話,你可以刪除它,你會節省一些性能。

如果您真的感覺內存不足問題,您可以使用上述的更多vm內存選項運行jvm。試一試。 :)

讓你的鍵的hashcode方法有效。您可以依靠其他apis,如Pojomatic來做那些事情。

+0

我想保持同步行爲,因爲我不希望一個線程讀取記錄,而另一個線程更新它的可能性。 – cduggan 2011-05-01 11:58:05

0

至於你的問題的最後一部分:

如何,我可以讓我的HashMap的寫入性能更高的任何建議?這也可能是我在存儲器中存儲這麼多數據的方法的限制嗎?

我使用了一個工具來看看應用程序在做什麼。它可以做堆和線程轉儲。它還有一個顯示cpu,類加載,線程,堆和perm gen的montior。它被稱爲Java VisualVM,它是jdk 1.6的一部分.exe位於jdk的bin文件夾中。我將使用它來跟蹤我們代碼中的一些線程問題。

HTH, James