MappedByteBufferのファイル削除の問題

3199 ワード

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を提供できるか分からない!
このリンクにも注目してください:http://bugs.sun.com/view_bug.do?bug_id=4724038