對於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方法來釋放內存,所以我們也可以直接在代碼里使用以上代碼來釋放內存。
