Pau*_*lor 8 java mappedbytebuffer
我刚刚在opensrc库代码中遇到一个错误,该代码分配了一个大缓冲区来修改大型flac文件,该错误只发生在使用Java 1.8.0_74 25.74-b02 32bit的具有3Gb内存的旧PC机上
最初我曾经只是分配一个缓冲区
ByteBuffer audioData = ByteBuffer.allocateDirect((int)(fc.size() - fc.position()));
Run Code Online (Sandbox Code Playgroud)
但有一段时间我有它
MappedByteBuffer mappedFile = fc.map(MapMode.READ_WRITE, 0, totalTargetSize);
Run Code Online (Sandbox Code Playgroud)
我(错误)的理解是映射缓冲区使用的内存少于直接缓冲区,因为整个映射缓冲区不必同时只在内存中使用的部分.但是这个答案说使用映射的字节缓冲区是一个坏主意,所以我不清楚它是如何工作的
Java Large File Upload抛出java.io.IOException:Map失败
完整的代码可以在这里看到
尽管映射缓冲区在任何一个时间点可能使用较少的物理内存,但它仍然需要等于缓冲区总(逻辑)大小的可用(逻辑)地址空间。更糟糕的是,它可能(可能)要求地址空间是连续的。无论出于何种原因,该旧计算机似乎无法提供足够的额外逻辑地址空间。两种可能的解释是 (1) 有限的逻辑地址空间 + 大量的缓冲存储器要求,以及 (2) 操作系统对可映射为 I/O 文件的内存量施加的一些内部限制。
关于第一种可能性,请考虑这样一个事实:在虚拟内存系统中,每个进程都在自己的逻辑地址空间中执行(因此可以访问完整的 2^32 字节寻址)。因此,如果在您尝试实例化MappedByteBufferJVM 进程的当前大小加上总(逻辑)大小MappedByteBuffer大于 2^32 字节(约 4 GB),那么您将遇到一个OutOfMemoryError(或者该类选择抛出的任何错误/异常,例如IOException: Map failed)。
关于第二种可能性,评估这种情况的最简单方法可能是在您尝试实例化MappedByteBuffer. 如果 JVM 进程分配的内存 + 所需的内存totalTargetSize远低于 2^32 字节上限,但您仍然收到“映射失败”错误,那么很可能是某些内部操作系统对内存映射文件大小的限制根本原因。
那么,就可能的解决方案而言,这意味着什么呢?
MappedByteBuffer. (看似合理,但可能无关紧要且绝对不切实际)totalTargetSize另外,您的问题案例到底是什么?
编辑:
经过一番挖掘后,很明显 IOException 是由于32 位环境中地址空间不足造成的。即使文件本身小于 2^32 字节,由于缺乏足够的连续地址空间,或者由于 JVM 中其他足够大的地址空间需求,同时结合大MappedByteBuffer请求,也可能发生这种情况(请参阅注释) 。需要明确的是,即使最初的原因是 ENOMEM ,仍然可以抛出 IOE 而不是 OOM 。此外,特别是较旧的 [在此处插入 Microsoft 操作系统] 32 位环境似乎存在问题(例如,例如)。
所以看起来你有三个主要选择。
MappedFileBuffer,但也以较小的块对文件进行操作,以解决地址空间限制。我将使用较小的块作为第三个的原因是因为在取消映射(示例MappedFileBuffer)时存在已确定且未解决的问题,这是您在处理每个块之间必须执行的操作,以避免达到 32 位由于累积映射的组合地址空间占用而导致的上限。 (注意:这仅适用于 32 位地址空间上限而不是某些内部操作系统限制的问题...如果是后者,则忽略本段) 您可以尝试此策略(删除所有引用,然后运行GC),但是您基本上会受到 GC 和底层操作系统如何与内存映射文件交互的影响。尝试或多或少地直接操作底层内存映射文件的其他潜在解决方法(示例)是极其危险的,并且受到 Oracle 的特别谴责(请参阅最后一段)。最后,考虑到 GC 行为无论如何都是不可靠的,而且官方文档明确指出“内存映射文件的许多细节未指定”,我不建议这样使用,无论您可能读到任何解决方法。MappedFileBufferMappedFileBuffer
因此,除非您愿意承担风险,否则我建议要么遵循 Oracle 的明确建议(第 1 点),要么使用不同的缓冲区类型将文件作为一系列较小的块进行处理(第 2 点)。
| 归档时间: |
|
| 查看次数: |
264 次 |
| 最近记录: |