2011-04-30 117 views
306

我在一個程序中創建了幾個(數十萬個)HashMap對象,每個對象都有幾個(15-20)個文本項。這些字符串在提交到數據庫之前都已收集(沒有分解成更小的數量)。java.lang.OutOfMemoryError:超出GC開銷限制

根據Sun的說法,錯誤發生在「如果在垃圾收集中花費了太多時間:如果超過總時間的98%花在垃圾收集上,並且小於2%的堆被恢復,OutOfMemoryError將被拋出。「

顯然,可以使用命令行參數傳遞給JVM爲

  • 增加堆大小,經由「-Xmx1024m」(或更多),或
  • 完全禁用錯誤校驗,通過「-XX:-UseGCOverheadLimit」。

第一種方法工作正常,第二種方法在另一個java.lang.OutOfMemoryError中結束,這次是關於堆。

因此,問題:對於特定的用例(即幾個小型的HashMap對象),是否有任何程序化的替代方案?例如,如果我使用HashMap clear()方法,問題就會消失,但存儲在HashMap中的數據也會消失! :-)

這個問題也是在related topic in StackOverflow.

+1

您可能需要改變你的算法,並使用一些更有效的數據結構。你能告訴我們你正在試圖實現哪種算法,這需要大量的HashMaps嗎? – Ankur 2011-04-30 03:59:47

+0

我只是閱讀非常大的文本文件(每個數十萬行),我無法控制它們,即它們不能被分解。對於每一行文本,都會構造一個HashMap,其中包含少數(實際上大約10個)小字符串值,並且一次又一次使用相同的數據庫字段名稱。理想情況下,我希望在將數據發送到數據庫之前能夠讀取整個文件。 – PNS 2011-04-30 09:32:34

+1

聽起來好像在將數據發送到數據庫之前讀取整個文件實際上是一個很糟糕的解決方案......實際上它根本無法工作,在可用內存的非常實際的限制之內。無論如何,你爲什麼要這麼做? 「一次又一次使用相同的數據庫字段名」是什麼意思?字段名稱作爲鍵或值?如果他們的字段是鍵,那麼只需使用一個數組,其中的字段被它的位置所隱含......如果它們是值,那麼在將它們添加到地圖之前將它們實習。這有助於瞭解數據是什麼。乾杯。基思。 – corlettk 2011-04-30 10:22:39

回答

154

你基本上運行內存流暢運行過程中進行討論。

  1. 指定更多的內存就像你提到的,嘗試一些在-Xmx512m之間像第一
  2. 工作與HashMap對象的小批量如果你有很多東西需要一次如果可能的話
  3. 處理:浮現在腦海中的選項重複字符串,用他們String.intern()將它們放入了HashMap
  4. 使用HashMap(int initialCapacity, float loadFactor)構造函數來調整你的情況之前
+1

我已經使用了接近HashMap的初始容量,所以程序在那裏幾乎是最優的。 – PNS 2011-04-30 09:56:43

+2

如果它的內存更多,是否有什麼理由不去?如果你使用諸如'-Xms128m -Xmx1024m'之類的東西,它實際上只會增長到最大。似乎最簡單的選擇。 – WhiteFang34 2011-04-30 10:03:40

+1

是的,我猜最快。我用intern()來表達一些可能重複的值,並且問題也消失了。 – PNS 2011-04-30 11:11:47

11

嗯......你要麼需要:

  1. 重新考慮你的算法&數據結構,使得它並不需要所有這些小HashMaps這樣。

  2. 創建一個門面,允許您根據需要對這些HashMaps進行內存進出。簡單的LRU緩存可能就是票證。

  3. 增加可供JVM使用的內存。如果有必要,即使購買更多內存可能是最快的,最便宜的解決方案,如果你有託管這個野獸的機器的管理。話雖如此:我通常不是「投入更多硬件」解決方案的粉絲,特別是如果在合理的時間範圍內可以考慮替代的算法解決方案。如果你不斷投入更多的硬件來解決這些問題,你很快就會遇到收益遞減的規律。

你究竟想要做什麼?我懷疑你的實際問題有更好的方法。

+0

看到我上面的評論。這個用例非常簡單,我正在尋找一種方法來處理整個大文件而不中斷流程。謝謝! – PNS 2011-04-30 09:52:47

5

如果您要創建數十萬個哈希映射,那麼您可能使用的遠遠超過實際需要;除非您使用大型文件或圖形,否則存儲簡單數據不應超出Java內存限制。

你應該嘗試重新思考你的算法。在這種情況下,我會提供更多有關該主題的幫助,但在提供有關問題背景的更多信息之前,我無法提供任何信息。

+0

看到我上面的評論。這個用例非常簡單,我正在尋找一種方法來處理整個大文件而不中斷流程。謝謝! – PNS 2011-04-30 09:55:28

23

爲了記錄,我們今天有同樣的問題。我們通過使用這個選項來修復它:

-XX:-UseConcMarkSweepGC 

顯然,這個修改了用於垃圾收集的策略,導致問題消失。

40

@takrl:該選項的默認設置是:

java -XX:+UseConcMarkSweepGC 

這意味着,這個選項是默認情況下不活躍。所以,當你說你使用的選項 「+XX:UseConcMarkSweepGC」 我假設你正在使用的語法如下:

java -XX:+UseConcMarkSweepGC 

這意味着你明確地激活這個選項。 對於@這個 document

+0

在我們使用-XX:+ UseConcMarkSweepGC的情況下,在高負載/高內存壓力的情況下,「OutOfMemoryError:GC開銷限制超出」錯誤的風險降低了一點,但另一方面它使用了更多的CPU,在正常負載情況下,執行時間延長5-10%。 – anre 2016-10-13 20:40:19

0

幫我擺脫這種的Java HotSpot VM Options正確的語法和默認設置error.This選項禁用 -XX:+ DisableExplicitGC

9

使用替代HashMap的實現(Trove)。標準的Java HashMap具有> 12倍的內存開銷。 人們可以閱讀詳細信息here

+6

\t net.sf.trove4j \t trove4j \t 3.0.3 Jeef 2014-05-16 21:43:02

2

在錯誤的情況下:

"Internal compiler error: java.lang.OutOfMemoryError: GC overhead limit exceeded at java.lang.AbstractStringBuilder"

增加Java堆空間爲2GB,即-Xmx2g.

9

整個結構不存儲在內存中,同時等待到達終點。

將中間結果寫入數據庫中的臨時表而不是hashmaps--在功能上,數據庫表等同於一個散列表,即既支持對數據的鍵控訪問,也不支持內存綁定,因此使用索引這裏的表格而不是hashmaps。

如果做得正確,你的算法甚至不應該注意到改變 - 正確的意思是使用一個類來表示表,甚至給它一個put(key,value)和一個get(key)方法就像一個hashmap 。

當中間表完成時,從中生成所需的sql語句而不是內存。

8

如果在垃圾收集中花費了太多時間,並行收集器將拋出OutOfMemoryError。特別是,如果超過98%的時間用於垃圾回收,並且只有不到2%的堆被回收,將會拋出OutOfMemoryError。此功能旨在防止應用程序長時間運行,而由於堆太小,因此很少或沒有進度。如有必要,可以通過在命令行中添加選項-XX:-UseGCOverheadLimit來禁用此功能。

+0

你從哪裏得到這個信息?我很感興趣,因爲它看起來非常非常正確。發現它...... ---> http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html#par_gc.oom – MaasSql 2015-04-17 12:41:04

59

以下爲我工作。只需添加下面的代碼片段:

dexOptions { 
     javaMaxHeapSize "4g" 
} 

要將build.gradle

android { 
    compileSdkVersion 23 
    buildToolsVersion '23.0.1' 

    defaultConfig { 
     applicationId "yourpackage" 
     minSdkVersion 14 
     targetSdkVersion 23 
     versionCode 1 
     versionName "1.0" 

     multiDexEnabled true 
    } 

    buildTypes { 
     release { 
      minifyEnabled false 
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 
     } 
    } 

    packagingOptions { 

    } 

    dexOptions { 
     javaMaxHeapSize "4g" 
    } 
} 
+0

更多詳情請參閱: http:// google .github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.DexOptions.html – 2015-12-16 07:53:20

+0

我做到了這一點,並且我仍然沒有足夠的空間。 Android Studio 2.2.3。 – 2017-02-14 01:45:21

+0

你的gradle版本是什麼? – 2017-02-14 13:33:52

1

對於我來說,增加使用-Xmx選項內存是解決辦法。

我有一個10g的文件在Java中讀取,每次我得到相同的錯誤。當top命令中的RES列中的值達到-Xmx選項中設置的值時發生這種情況。然後通過增加內存使用-Xmx選項一切都很好。

還有一點。當我在我的用戶帳戶中設置JAVA_OPTSCATALINA_OPTS並再次增加內存量時,我得到了同樣的錯誤。然後,我在代碼中輸出了這些環境變量的值,這些變量賦予了我設置的不同值。原因是Tomcat是這個過程的根源,然後因爲我不是傻瓜,我讓管理員增加Tomcat中catalina.sh的內存。

3

修復內存泄漏與輪廓工具幫助像Eclipse MATVisualVM

隨着JDK 1.7.x或更高版本的應用程序,使用G1GC其垃圾回收花費10%不同於其他GC算法2%。

除了設置堆內存與-Xms1g -Xmx2g,嘗試`

-XX:+UseG1GC 
-XX:G1HeapRegionSize=n, 
-XX:MaxGCPauseMillis=m, 
-XX:ParallelGCThreads=n, 
-XX:ConcGCThreads=n` 

看一看oracle文章微調這些參數。

在SE相關G1GC一些問題:

Java 7 (JDK 7) garbage collection and documentation on G1

Java G1 garbage collection in production

Agressive garbage collector strategy

2

你需要增加JDeveloper中的內存大小去的setDomainEnv.cmd。

set WLS_HOME=%WL_HOME%\server 
set XMS_SUN_64BIT=256 
set XMS_SUN_32BIT=256 
set XMX_SUN_64BIT=3072 
set XMX_SUN_32BIT=3072 
set XMS_JROCKIT_64BIT=256 
set XMS_JROCKIT_32BIT=256 
set XMX_JROCKIT_64BIT=1024 
set XMX_JROCKIT_32BIT=1024 

if "%JAVA_VENDOR%"=="Sun" (
    set WLS_MEM_ARGS_64BIT=-Xms256m -Xmx512m 
    set WLS_MEM_ARGS_32BIT=-Xms256m -Xmx512m 
) else (
    set WLS_MEM_ARGS_64BIT=-Xms512m -Xmx512m 
    set WLS_MEM_ARGS_32BIT=-Xms512m -Xmx512m 
) 
and 

set MEM_PERM_SIZE_64BIT=-XX:PermSize=256m 
set MEM_PERM_SIZE_32BIT=-XX:PermSize=256m 

if "%JAVA_USE_64BIT%"=="true" (
    set MEM_PERM_SIZE=%MEM_PERM_SIZE_64BIT% 

) else (
    set MEM_PERM_SIZE=%MEM_PERM_SIZE_32BIT% 
) 

set MEM_MAX_PERM_SIZE_64BIT=-XX:MaxPermSize=1024m 
set MEM_MAX_PERM_SIZE_32BIT=-XX:MaxPermSize=1024m 
5

如果你有java8,並且可以使用G1垃圾收集,然後運行你的應用程序:

-XX:+UseG1GC -XX:+UseStringDeduplication 

這告訴G1找到類似的字符串,只保留一個它們在內存中,而其他的只是指向內存中該字符串的指針。

當你有很多重複的字符串時,這很有用。此解決方案可能或不可行,並取決於每個應用程序。

更多信息上:
https://blog.codecentric.de/en/2014/08/string-deduplication-new-feature-java-8-update-20-2/ http://java-performance.info/java-string-deduplication/

+0

謝謝喬治。幫助我編譯Apache Camel: 導出MAVEN_OPTS =「 - Xms3000m -Xmx3000m -XX:+ UseG1GC -XX:+ UseStringDeduplication」 – 2016-09-01 11:23:18

+0

歡迎大家關注CPU使用情況,因爲G1 GC對它的要求更高一些。 – 2016-09-01 13:16:29

2

對於下面這段代碼在使用你的應用程序文件的gradle關閉的android下。

dexOptions { javaMaxHeapSize 「4G」 }

相關問題