https://www.cnblogs.com/swbzmx/p/5992592.html
对于MappedByteBuffer映射的文件,直接调用删除方法是无法删掉的。原因就是这部分内存的回收靠的是垃圾回收机制。
而垃圾回收的时间是我们无法控制的,这就导致了文件始终被占用。
例子:
FileInputStream fis = null; File f = new File("a.txt"); try { fis = new FileInputStream(f); FileChannel fc = fis.getChannel(); // 把文件映射到内存 MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, (int) fc.size()); // TODO fc.close(); fis.close(); } catch (FileNotFoundException ex) { System.err.println("Error! " + ex.getMessage()); System.exit(2); } catch (IOException e) { System.err.println("Error! " + e.getMessage()); System.exit(3); } // 删除文件 boolean deleted = f.delete(); if (!(deleted)) { System.err.println("Could not delete file " + f.getName()); }
删除文件失败!原因是没有释放内存。
究其原因,FileChannel在调用了map方法,进行内存映射得到MappedByteBuffer,但是没有提供unmap方法(),释放内存。事实上,unmap方法是在FileChannelImpl类里实现的,是个私有方法。在finalize延迟的时候,unmap方法无法调用,在删除文件的时候就会因为内存未释放而失败。不过可以通过显示的调用unmap方法来释放内存。
以下代码则可以保证可以成功删除文件:
try { File f = File.createTempFile("Test", null); f.deleteOnExit(); RandomAccessFile raf = new RandomAccessFile(f, "rw"); raf.setLength(1024); FileChannel channel = raf.getChannel(); MappedByteBuffer buffer = channel.map( FileChannel.MapMode.READ_WRITE, 0, 1024); channel.close(); raf.close();
// 加上这几行代码,手动unmap //////////////////////////////////////// Method m = FileChannelImpl.class.getDeclaredMethod("unmap", MappedByteBuffer.class); m.setAccessible(true); m.invoke(FileChannelImpl.class, buffer);
/////////////////////////////////////////
if (f.delete()) System.out.println("Temporary file deleted: " + f); else System.err.println("Not yet deleted: " + f); } catch (Exception ex) { ex.printStackTrace(); }
其实最终都是调用了Cleaner类的,clean方法。
我们从FileChannelImpl的unmap方法来入手
private static void unmap(MappedByteBuffer bb) { Cleaner cl = ((DirectBuffer)bb).cleaner(); if (cl != null) cl.clean(); }
这是一个私有方法,调用了Cleaner的clean方法来释放内存,所以我们也可以直接在代码里使用以上代码来释放内存。
__________________________________________________________________________________________________________________________
Java之nio MappedByteBuffer的资源释放问题
http://t.zoukankan.com/zolo-p-5849320.html
使用nio的MappedByteBuffer映射内存, 在最后执行File.delete()方法的时候, 返回false, 即文件没有被删除.
原因是MappedByteBuffer在内存中也会创建文件的一个句柄, 所以必须先释放MapppedByteBuffer之后才能执行文件的删除操作.
如果使用SUN的JDK, 可以使用:
public static void clean(ByteBuffer bb) {
if (bb == null)
return;
Cleaner cleaner = ((DirectBuffer) bb).cleaner();
if (cleaner != null)
cleaner.clean();
}
如果使用SUN以外的JDK, 例如JRockit, 必须预防该API不存在, 可以借助反射机制来完成兼容代码:
public static void clean(final MappedByteBuffer mbb) { if (mbb == null) { return; } AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { try { Method cleanerMethod = mbb.getClass().getDeclaredMethod("cleaner", new Class[0]); if (cleanerMethod != null) { cleanerMethod.setAccessible(true); Object cleanerObject = cleanerMethod.invoke(mbb, new Object[0]); Method cleanMethod = cleanerObject.getClass().getDeclaredMethod("clean", new Class[0]); if (cleanMethod != null) { cleanMethod.invoke(cleanerObject, new Object[0]); } } } catch (Exception e) { logger.error("关闭MappedByteBuffer句柄错误!", e); } return null; } }); }
使用AccessController目的是为了启动SecurityManager的时候也有足够的私权去释放内存句柄.