考虑创建5-6个线程的应用程序,每个线程在循环中为5mb页面大小分配MappedByteBuffer。

MappedByteBuffer b = ch.map(FileChannel.MapMode.READ_ONLY, r, 1024*1024*5);

迟早,当应用程序处理大文件时,会抛出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)

根据规范,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();
}

如果您在丢弃前调用此方法,则不会耗尽虚拟内存。

也许您可以减少创建较大的ByteBuffer的次数(除非您有大量文件),而创建MappedByteBuffer并不是免费的(在某些计算机上大约需要50微秒)

10-07 13:09