熟悉nio的朋友都知道,MappedByteBuffer大幅提高了IO效率,但却有个比较严重的问题。
看如下测试代码:
public class TestMappedByteBufferDeleteFile {
File testFile;
/**
* 创建测试文件
* @throws URISyntaxException
* @throws IOException
*/
@Before public void createFile() throws URISyntaxException, IOException {
testFile = new File(this.getClass().getResource(".").getPath() + "/test.txt");
if(!testFile.exists()) {
testFile.createNewFile();
}
FileOutputStream fos = new FileOutputStream(testFile);
fos.write("TestMappedByteBufferDeleteFile".getBytes());
fos.close();
}
/**
* 测试使用MappedByteBuffer后直接删除文件
* @throws IOException
*/
@Test public void testDeleteFile() throws IOException {
map(testFile);
testFile.delete();
Assert.assertEquals(false, testFile.exists());
}
/**
* 映射文件,返回MappedByteBuffer
* @param file
* @return
* @throws IOException
*/
public MappedByteBuffer map(File file) throws IOException {
FileInputStream in = new FileInputStream(file);
FileChannel ch = in.getChannel();
MappedByteBuffer buffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
ch.close();
in.close();
return buffer;
}
}
Junit运行,结果断言失败,文件没删除成功。
解决方法是加上如下代码:
/**
* 清理MappedByteBuffer句柄
* @param buffer
*/
public static void clean(final MappedByteBuffer buffer) {
if (buffer == null) {
return;
}
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
try {
Method getCleanerMethod = buffer.getClass().getMethod(
"cleaner", new Class[0]);
if (getCleanerMethod != null) {
getCleanerMethod.setAccessible(true);
Object cleaner = getCleanerMethod.invoke(buffer,
new Object[0]);
Method cleanMethod = cleaner.getClass().getMethod(
"clean", new Class[0]);
if (cleanMethod != null) {
cleanMethod.invoke(cleaner, new Object[0]);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
然后将testDeleteFile() 作如下修改:
/**
* 测试使用MappedByteBuffer后调用clean方法后删除文件
* @throws IOException
*/
@Test public void testDeleteFile() throws IOException {
MappedByteBuffer buffer = map(testFile);
clean(buffer);
testFile.delete();
Assert.assertEquals(false, testFile.exists());
}
再运行Junit,成功!
之所以出现这种情况,是因为文件句柄没有被清除,还被MappedByteBuffer占据,导致外部无法操作。而要清除,得等GC收集了。
不知Sun何时可以提供MappedByteBuffer的unmap!