簡體   English   中英

在tenured堆空間中處理許多符合GC的大型對象

[英]Dealing with many large GC-eligible objects in tenured heap space

我有一個生成大型結果對象並將它們放入隊列的應用程序。 多個工作線程創建結果對象並對其進行排隊,並且單個寫入器線程將對象降級,將它們轉換為CSV,並將它們寫入磁盤。 由於I / O和結果對象的大小,編寫結果所需的時間比生成它們要長得多。 此應用程序不是服務器,它只是一個運行大量請求和完成的命令行應用程序。

我想減少應用程序的總內存占用量。 使用堆分析工具( IBM HeapAnalyzer ),我發現在程序終止之前,大多數大型結果對象仍然在堆上,即使它們已被排隊並且沒有其他引用它們。 也就是說,它們都是根對象。 它們占用了大部分堆空間。

對我來說,這意味着當他們仍然在隊列中時,他們將它變成了終身堆空間。 由於在運行期間沒有觸發完整的GC,因此它們仍然存在。 我意識到它們應該是終身受讓的,否則我會在Eden空間里來回復制它們,而它們仍在隊列中,但同時我希望我能做些什么來幫助擺脫它們之后排隊,缺少調用System.gc()

我意識到擺脫它們的一種方法是簡單地縮小最大堆大小並觸發完整的GC。 但是,該程序的輸入大小差異很大,我希望所有運行都有一個-Xmx設置。

為澄清添加 :這是一個問題,因為在Eden中實際編寫對象時也存在大量內存開銷(主要是String實例,它們在堆分析中也顯示為根)。 因此,伊甸園經常出現輕微的GC。 如果結果對象沒有在終身空間中閑逛,則這些頻率會降低。 可以說我的真正問題是伊甸園的輸出開銷,我正在研究這個問題,但我想同時追求這個終身問題。

在我研究這個時,我應該關注哪些特定的垃圾收集器設置或程序化方法? 注意我使用的是JDK 1.8。

回答更新 :@maaartinus提出了一些很好的建議,幫助我避免首先排隊(並因此使用)大型物體。 他還建議限制隊列,這肯定會減少我現在正在排隊的時間(結果對象的CSV byte[]表示)。 線程計數和隊列邊界的正確組合肯定會有所幫助,盡管我沒有嘗試過,因為問題基本上消失了,因為它找到了一種不首先使用大對象的方法。

我對GC相關的解決方案持懷疑態度,但看起來你正在創建一個你不需要的問題:

多個工作線程創建結果對象並將它們排隊,並且單個編寫器......

...寫出結果需要比生成它們更長的時間......

所以它看起來應該是另一回事:單一制作人和許多消費者保持游戲均勻。

多個作者可能不會給你太多加速,但如果可能的話,我會嘗試一下。 只要你為結果使用有隊列,生產者的數量並不重要(我假設他們沒有大量的輸入,因為你沒有提到它)。 這個有界的隊列還可以確保對象永遠不會太舊。

在任何情況下,您都可以使用多個CSV轉換器,因此可以通過大型Stringbyte[]ByteBuffer或其他任何方式(假設您想在內存中進行轉換)有效地替換大對象。 關於緩沖區的好處是你可以回收它(所以它終止的事實不再是問題)。

你也可以使用一些非托管內存,但我真的不相信它是必要的。 簡單地限制隊列就足夠了,除非我遺漏了什么。

順便說一句,最便宜的解決方案往往是購買更多內存。 真的,一小時的工作值幾千兆字節。

更新

我應該擔心多個編寫器線程之間的爭用多少,因為他們都會共享一個線程安全的Writer?

我可以想象兩種問題:

  • 原子性:雖然同步確保每個執行的操作以原子方式發生,但這並不意味着輸出有任何意義。 想象一下多個編寫器,每個編寫器生成一個CSV,結果文件應包含所有CSV(按任意順序)。 使用PrintWriter會使每一行保持完整,但它會混合它們。

  • 並發:例如, FileWriter執行從char s到byte s的轉換,這可能在此上下文中以同步塊結束。 這可能會減少並行性,但由於IO似乎是瓶頸,我猜,這沒關系。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM