use*_*512 5 java nio bytebuffer filechannel out-of-memory
考虑应用程序,它创建5-6个线程,循环中的每个线程为5mb页面大小分配MappedByteBuffer.
MappedByteBuffer b = ch.map(FileChannel.MapMode.READ_ONLY, r, 1024*1024*5);
Run Code Online (Sandbox Code Playgroud)
迟早,当应用程序使用大文件时,会抛出oom
java.io.IOException: Map failed at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:758)
Caused by: java.lang.OutOfMemoryError: Map failed
at sun.nio.ch.FileChannelImpl.map0(Native Method)
at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:755)
Run Code Online (Sandbox Code Playgroud)
根据规范,MappedBuffer应该在GC本身时立即配置直接内存.看起来问题是,MappedBuffer-s的GC编辑太晚了,后来直接内存完成了.
如何避免这种情况?可能会说MappedBuffer隐式处理或使用某种MappedBuffer池
您可以通过直接清理映射的字节缓冲区来避免触发GC.
public static void clean(ByteBuffer bb) {
if(bb == null) return;
Cleaner cleaner = ((DirectBuffer) bb).cleaner();
if(cleaner != null) cleaner.clean();
}
Run Code Online (Sandbox Code Playgroud)
如果您在丢弃之前调用此方法,则不会耗尽虚拟内存.
也许您可以减少创建较大的ByteBuffers(除非您有大量文件)创建MappedByteBuffer不是免费的(在某些机器上大约需要50微秒)
也许将WeakHashMap这些集中起来MappedBuffers会起作用。
但在您猜测根本原因之前,我建议您将应用程序连接到Visual VM 1.3.3并安装所有插件,这样您就可以准确地看到导致 OOM 错误的原因。您假设这些 MappedBuffer 正在执行此操作,但对于 5-6 个线程,每个 MappedBuffer 仅 5MB - 总共 25-30MB。
有数据比猜测更好。Visual VM 将为您提供它。