如何正确关闭 MappedByteBuffer

Java关于mmap的一个bug。由于FileChannel调用了map方法做内存映射,但是没提供对应的unmap方法释放内存,导致内存一直占用该文件。解决办法如下:

java9之前

public class Test
{
    public static void main(String[] args) throws Exception {
        String filePath = "D:/temp/file";
        RandomAccessFile file = new RandomAccessFile(filePath, "rw");
        FileChannel chan = file.getChannel();
        try {
            MappedByteBuffer buffer = chan.map(FileChannel.MapMode.READ_WRITE, 0, 128);
            // Do something
            buffer.putInt(4);
            buffer.force();
            Cleaner cleaner = ((sun.nio.ch.DirectBuffer) buffer).cleaner();
            if (cleaner != null) {
                cleaner.clean();
            }
        } finally {
            chan.close();
            file.close();
            System.out.println("File closed");
        }
        System.out.println("Press any key...");
        System.in.read();
        System.out.println("Finished");
    }
}

如果不能直接使用sun.nio.ch.DirectBuffer和Cleaner,可以试试:

public void clean(final ByteBuffer buffer) {
    AccessController.doPrivileged((PrivilegedAction) () -> {
        try {
            Field field = buffer.getClass().getDeclaredField("cleaner");
            field.setAccessible(true);
            Object cleaner = field.get(buffer);

            Method cleanMethod = cleaner.getClass().getMethod("clean");
            cleanMethod.invoke(cleaner);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    });
}
 
 

java9+

MappedByteBuffer buffer = ...
// Java 9+ only:
Class unsafeClass = Class.forName("sun.misc.Unsafe");
Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Object unsafe = unsafeField.get(null);
Method invokeCleaner = unsafeClass.getMethod("invokeCleaner", ByteBuffer.class);
invokeCleaner.invoke(unsafe, buffer);

你可能感兴趣的:(如何正确关闭 MappedByteBuffer)