2012-07-27 38 views
4

在啓動時本地運行的應用程序(Spring,JPA Hibernate,Sybase 12,Webapp)佔用基於VisualVM的256MB堆空間的40MB。當我觸發一個返回超過70,000行的文本數據(文本數據沒有斑點)時,堆空間圖形將最高可達256MB,並且會丟失內存。 我已經通過setMaxResults(limit)解決了這個問題。但是,當我查詢相同的數據,複製粘貼到文本文件並保存到文件系統時,我可以看到文本的大小隻有26MB。從數據庫中加載26MB文本數據消耗258MB的JVM堆棧

因此,實際上,通過從數據庫中加載26MB數量的文本消耗216MB(從256-40),在內存不足的情況下耗費190MB內存(012MB)也許這將是框架,但我不知道它是如何消耗超過實際加載的數據...

* *再次注意,我解決了這與setMaxResults(限制),我的問題不是做什麼,而是爲什麼,爲了教育目的。

+1

你的意思是190Mb是用來表示數據還是190Mb在讀取過程中被分配? – 2012-07-27 03:21:33

+1

我的意思是190MB在發生內存不足錯誤時全部用完。來自數據庫的數據僅爲26MB,基於複製粘貼並將其保存到文本文件。 – 2012-07-27 03:23:29

回答

5

有些事情要考慮:

你的操作系統可能使用每字符編碼的8位來存儲文本文件。內部的Java字符串全部編碼爲每個字符16位,這裏的空間加倍。

只有少數數字的數字將被編碼爲小於數字的文字。例如,'1'是文本文件中的一個字節字符,但長度爲1的值是內存中的八倍。

將從hibernate中取出SQL結果集中的值並將其映射到您的java對象上。它可能需要將結果集的內容包裝/翻譯成您在映射中定義的類型。

如果您的每個實體數據對於大量實體來說實際上很小,那麼對象開銷大小與數據大小的比率顯然很高。

如果您在收藏中有小塊數據,收藏的大小可以相對於數據快速加起來。在極端的例子中,如果你有一個或兩個字符串LinkedList,那麼192位就會被實際數據的每16-32位指針所消耗。在數組列表中,指針指向16-32位數據仍然是64位。 (當然假設是64位操作系統)。

你在hibernate中加載的每個對象都被「追蹤」,以便在所謂的L1緩存中進行髒檢查。內部數據結構和用於執行此操作的設備相對於具有少量數據的大量實體的數據大小確實存在相當大的開銷。

-

所以數據的26MB已經在Java內存數據的52MB,假設它是所有的字符串,沒有數字,沒有日期,這將是更大的,否則。

然後,如果它被分成許多小塊,700,000個小字符串而不是1,000個真正長的字符串,那麼數據結構開銷的大小是實際數據大小的三倍是完全合理的,推動您輕鬆超過200MB 。

+0

感謝您提供此洞察。 – 2012-07-27 08:38:53

2

各種各樣的事情。

讓我們考慮一下,例如你的行有10個文本列,它們被表示爲一個帶有10個字符串字段的簡單Java Bean。

一個字符串有4個字段:一個char []和3個整數。

一個字符串是對象的後代,它有1個int和對其類的引用。

在一個64位的JVM上,這些引用很可能是8個字節(但不一定,但我們會堅持爲爭辯)。

一個10個字符的字符串將會有一個char [10]和3個ints,每個字符串都是4個字節。

char [10]是一個指向數組的指針。一個數組必須跟蹤它的長度,這可能是另外4個字節,它也是一個Object(因此類指針和另一個int)加上數據。但Java中的字符在內部表示爲UTF-16,每個字符2個字節。所以,10個字符的實際數組需要24個字節。對該數組的引用是一個指針。因此,單個String實例是:對象爲8 + 4,對於字符串本身爲8 + 4 + 4 + 4,對於實際數據爲8 + 4 + 20,或者62字節。

您的bean有10個字符串字段,加上擴展對象,所以8 + 4 +(10 * 8)。

因此,對於100個字符的文本,數據庫中的單個行是8 + 4 +(10 * 8)+(10 * 62),它等於712個字節。

這些不是完美的數字,我不能專門講述數組是如何存儲的,並且64位JVM上的對象引用可能不是8個字節。

但它給你一些有關開銷的想法。這僅僅是爲了你的原始數據。如果你將這些行存儲在一個ArrayList中,那麼就有70,000 * 8只是指向你的對象 - 560K只是這個結構。

+0

感謝您的支持 – 2012-07-27 08:40:15