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<Object>) () -> {
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);