![](/img/trans.png)
[英]Android how to avoid i/o problems when periodically writing to a file on internal memory
[英]How do I avoid mapFailed() error when writing to large file on system with limited memory
我剛剛在我的opensrc庫代碼中遇到錯誤,該代碼分配了一個較大的緩沖區以對大型flac文件進行修改,該錯誤僅發生在使用Java 1.8.0_74 25.74-b02 32位且內存為3Gb的舊PC機上
最初我只是分配一個緩沖區
ByteBuffer audioData = ByteBuffer.allocateDirect((int)(fc.size() - fc.position()));
但是一段時間以來
MappedByteBuffer mappedFile = fc.map(MapMode.READ_WRITE, 0, totalTargetSize);
我的(錯誤)理解是,映射緩沖區使用的內存比直接緩沖區使用的內存少,因為整個映射緩沖區不必同時在內存中,而只在使用一部分。 但是這個答案說使用映射的字節緩沖區是一個壞主意,所以我不知道如何工作
Java大文件上傳引發java.io.IOException:映射失敗
完整的代碼可以在這里看到
盡管映射的緩沖區在任何時間點可能使用較少的物理內存,但它仍然需要等於緩沖區的總(邏輯)大小的可用(邏輯)地址空間。 使情況更糟的是,它可能(可能)要求地址空間是連續的。 無論出於何種原因,那台舊計算機似乎都無法提供足夠的額外邏輯地址空間。 兩種可能的解釋是(1)有限的邏輯地址空間+大量的緩沖內存要求,以及(2)OS對可映射為I / O文件的內存量施加了一些內部限制。
關於第一種可能性,請考慮以下事實:在虛擬內存系統中,每個進程都在其自己的邏輯地址空間中執行(因此可以訪問整個2 ^ 32字節的地址)。 因此,如果在嘗試實例化MappedByteBuffer
時間點上,JVM進程的當前大小加上MappedByteBuffer
的總(邏輯)大小大於2 ^ 32字節( MappedByteBuffer
GB),則您將遇到OutOfMemoryError
(或類選擇替代的任何錯誤/異常,例如IOException: Map failed
)。
關於第二種可能性,可能最簡單的評估方法是在嘗試實例化MappedByteBuffer
對程序/ JVM進行概要分析。 如果JVM進程分配的內存+所需的totalTargetSize
遠低於2 ^ 32字節的上限,但是您仍然收到“映射失敗”錯誤,則可能是內部操作系統對內存映射文件的大小有一些限制根本原因。
那么,這意味着什么可能的解決方案呢?
MappedByteBuffer
的生命周期內具有盡可能低的內存占用。 (合理,但可能無關緊要,而且絕對不切實際) 另外,針對您的問題案例的totalTargetSize
到底是什么?
編輯:
經過一些挖掘后,很明顯IOException是由於32位環境中的地址空間用盡了 。 當文件本身是根據2 ^ 32字節或者由於缺乏足夠的連續的地址空間,或由於在同一時間與大合並在JVM其它足夠大的地址空間的要求這甚至可以發生MappedByteBuffer
請求( 見注釋 )。 需要說明的是, 即使最初的原因是ENOMEM ,仍然可以拋出IOE而不是OOM。 而且,特別是在較舊的[在此處插入Microsoft OS] 32位環境中似乎存在問題( 例如 , example )。
因此,看來您有三個主要選擇。
MappedFileBuffer
,但也要對文件進行較小的操作以解決地址空間限制。 我MappedFileBuffer
在較小的塊中使用MappedFileBuffer
作為第三,是因為在取消MappedFileBuffer
存在的既定問題和未解決的問題( 示例 ),這是您在處理每個塊之間必須要做的,以避免碰到32位上限,這是由於累積映射的組合地址空間占用空間所致。 (注意:僅在32位地址空間上限而不是某些內部操作系統限制是問題的情況下才適用...如果是后者,則忽略此段。)您可以嘗試此策略 (刪除所有引用,然后運行GC),但實際上,您將受制於GC和您的底層操作系統如何在內存映射文件上進行交互。 嘗試直接或多或少地直接操縱底層內存映射文件的其他可能的解決方法( 示例 )極其危險,並且受到Oracle的特別譴責( 請參閱最后一段 )。 最后,考慮到GC行為仍然是不可靠的,此外,官方文檔明確指出“ 未指定內存映射文件的許多詳細信息 ”,因此我不建議您使用MappedFileBuffer
這樣的方法,無論您可能會遇到任何變通辦法關於。
因此,除非您願意冒險,否則我建議要么遵循Oracle的明確建議(第1點),要么使用不同的緩沖區類型將文件作為一系列較小的塊處理(第2點)。
當您分配緩沖區時,您基本上會從操作系統中獲得虛擬內存塊(並且此虛擬內存是有限的,理論上最大的是您的RAM +配置的任何交換-其他任何其他程序和OS首先搶占的東西)
內存映射只會將磁盤文件上占用的空間添加到虛擬內存中(好吧,有一些開銷,但不是很多)-因此您可以獲得更多的空間。
這些都不是必須一直存在於RAM中,它的一部分可以在任何給定時間被換出到磁盤中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.