2008-08-26 50 views
3

我使用ByteBuffersFileChannels將二進制數據寫入文件。當爲大文件或連續多個文件做這件事時,我會遇到一個例外情況OutOfMemoryError。 我讀過其他地方,使用Bytebuffers與NIO已損壞,應該避免。你們有沒有遇到這種問題,並找到一種解決方案來高效地將大量二進制數據保存在java文件中?如何在使用Bytebuffers和NIO時避免OutOfMemoryError?

jvm選項-XX:MaxDirectMemorySize要走的路嗎?

回答

6

我會說不要創建一個包含所有數據的巨大的ByteBuffer。創建一個小得多的ByteBuffer,填充數據,然後將這些數據寫入FileChannel。然後重置ByteBuffer並繼續,直到寫入所有數據。

1

如果您在隨機方式訪問文件(讀到這裏,跳過,寫在那裏,回遷),那麼你有問題;-)

但是如果你只寫大文件時,應認真考慮使用流。可以直接使用java.io.FileOutputStream來在字節之後寫入文件字節,或者將其封裝在任何其他流(即DataOutputStream,ObjectOutputStream)中以方便編寫浮點數,整數,字符串或甚至可序列化的對象。存在用於讀取文件的類似的類。

流爲您提供了方便操作任意大的文件(幾乎)任意小的內存。在絕大多數情況下,它們是訪問文件系統的首選方式。

4

查看Java的Mapped Byte Buffers,也稱爲'直接緩衝區'。基本上,該機制使用操作系統的虛擬內存分頁系統將緩衝區直接映射到磁盤。操作系統將管理將字節快速移動到磁盤和內存的磁盤和內存,而且您不必擔心更改虛擬機選項。這也可以讓你利用NIO比傳統的基於Java流的I/O改進的性能,沒有任何奇怪的黑客。

只有兩個,我能想到的漁獲量:

  1. 在32位系統上,僅限於總略低於4GB被映射的字節緩衝區。 (這實際上是我的應用程序的限制,現在我運行在64位體系結構上。)
  2. 實現是特定於JVM的,不是必需的。我使用Sun的JVM,沒有問題,但是YMMV。

柯克佩珀(有點著名的Java性能專家)參與與網站,www.JavaPerformanceTuning.com,有一些細節MBB:NIO Performance Tips

+0

感謝您指出所有映射的字節緩衝區(僅適用於我的應用程序或全部在OS上)有限制。在我的情況下,即使我嘗試使用一個MappedByteBuffer作爲一個文件,我也會遇到愚蠢的OutOfMemoryException 1.6GB!但爲什麼?我該如何找出剩餘空間有多大?幫幫我! – Zordid 2012-09-21 13:58:35

0

前兩次的反應似乎很合理。至於命令行開關是否可以工作,取決於您的內存使用量達到極限的速度。如果您沒有足夠的內存和虛擬內存可用,至少可用內存的三倍,那麼您將需要使用其中一種替代建議。

0

使用transferFrom方法應該可以解決這個問題,假設你按照先前的答案指出的那樣逐步寫入通道而不是全部寫入通道。

0

這可能取決於特定的JDK供應商和版本。

某些Sun JVM中存在GC錯誤。直接內存短缺不會觸發主堆中的GC,但直接內存由主堆中的垃圾直接ByteBuffers鎖定。如果主堆大部分是空的,他們很多都不會被收集很長時間。

即使您自己沒有使用直接緩衝區,這也可以將其燒燬,因爲JVM可能正在爲您創建直接緩衝區。例如,將一個非直接的ByteBuffer寫入一個SocketChannel會在封面下創建一個直接緩衝區,用於實際的I/O操作。

解決方法是自己使用少量直接緩衝區,並將其保留以供重用。

相關問題