2014-09-27 52 views
2

我有一塊混雜着Scala代碼的複雜Java代碼,它讀入輸入文件,並在逐行處理行時創建大量數據結構,包括輸入中長度最大爲10的所有子字符串的散列表。Java在產生OutOfMemory錯誤之前等待了20個小時?

我最初使用-Xmx4g開關運行代碼,代碼花費了20小時,然後才返回OOM錯誤,但沒有完成整個文件。

然後我運行代碼-Xmx32g,代碼處理文件中的所有行,並在8分鐘內,然後繼續處理讀取的數據結構。 8分鐘後,Java使用的駐留內存大約爲21GB。

我的問題是:爲什麼Java在8分鐘內沒有返回OOM錯誤?它在20小時內做了什麼?

+5

不斷垃圾收集。 – 2014-09-27 19:54:44

+0

'String.substring()'的內存消耗高度依賴於Java版本(在1.7.0_06之前或之後),以及您的子字符串是否覆蓋原始字符串的大部分或僅包含原始字符串的一小部分。 Pre'1.7.0_06'子字符串由原始字符串的相同char []支持 - 整個字符串保留在內存中,但是被所有子字符串共享。用'1.7.0_06'子串創建新的字符數組。 – 2014-09-27 21:49:37

+0

@FabianBarney:我想知道他們爲什麼這麼做。舊的方法似乎更明智。 – 2014-09-27 22:35:09

回答

3

根據您的描述,我敢打賭,您遇到的OutOfMemoryError被標記爲「java.lang.OutOfMemoryError:GC overhead overhead exceeded exceeded」。獨立,如果我的賭注是正確的,形勢的描述完全一致到什麼下面發生的事情:

  • 您加載一些數據到內存
  • GC踢和清理一些。在每次GC完成它的工作時,它暫停了應用程序線程。
  • 您加載了一些,觸發了更多的GC,但在每次GC之後,恢復的內存越來越少。
  • 垃圾回收的頻率增加了,直到很少有真正的進展
  • JVM有一個內置的安全網來捕捉這些情況 - 每當你花費超過98%的GC時間並且只能恢復2%或更少的堆,則拋出「超出java.lang.OutOfMemoryError:GC開銷限制」錯誤。

要檢測這種情況,可以打開GC日誌(例如,-verbose:gc或-XX:+ PrintGCDetails),並留意暫停時間。

+0

)謝謝!您能否提供有關JVM「安全網」的更多詳細信息?在什麼時間範圍內是「 98%「計算?最後5分鐘,最後一小時?這是JVM的最新補充嗎? – Sarkom 2014-09-30 15:36:28

+1

在Oracle公共文檔中,沒有詳細提及所用的時間段 - 所以真相的最佳來源是挖掘OpenJDK源代碼代碼來找到答案,但這不是最近的補充,至少在JDK6中已經出現了 – Ivo 2014-09-30 21:24:54

0

Java有一個垃圾收集器線程。如果它發現它可以收集足夠的垃圾來釋放空間,它會這樣做。

因此,我建議使用visual VM並監視下次垃圾收集的時間。

1

垃圾收集是非確定性和複雜的。結合JIT編譯等其他內容,精確的內存使用模式很難預測和複製。

你提到它包括Scala?這使事情變得更有趣。 「好」的功能結構可能會產生比看起來更多的垃圾。即使像在Option-s中包裝對象那樣的小事情也需要額外的內存來清理。

相關問題