MappedByteBuffer文件句柄釋放問題


問題描述

現在很多java代碼中都會用到內存映射的概念。文件映射的方式比輸入輸出流的方式快很多。但是在使用的過程中,正常地調用了FileChannel的force和close方法后,重命名文件或刪除文件還會失敗。主要原因還是文件的句柄沒有釋放。

問題分析及解決

文件句柄如果被持有,就相當於jvm虛擬機中有一個指向文件的指針,所以重命名文件和刪除文件都會導致失敗。而MappedByteBuffer沒有提供unmap方法,且JDK中關於這個unmap方法的bug一直處於開放狀態。在網上查找說的最多的就是利用反射調用FileChannelImpl的unmap方法釋放內存:

    private static void unmap(MappedByteBuffer var0) {
        Cleaner var1 = ((DirectBuffer)var0).cleaner();
        if (var1 != null) {
            var1.clean();
        }
    }

反射調用的代碼不在寫了。而且親自測試了的確可以保證文件的刪除。
利用反射調用的原因是這個方法時私有靜態的。但是換一個思路,既然已經知道了內部代碼的實現,不如自己寫一個unmap方法將內部的代碼copy出來不是更好嗎?所以

private void unmap(MappedByteBuffer var0) {
        Cleaner var1 = ((DirectBuffer)var0).cleaner();
        if (var1 != null) {
            var1.clean();
        }
    }

在調用buffer的force方法后,再調用該unmap方法,就可以重命名文件了。

File file = new File(rootPath + fileNum + ".txt");
RandomAccessFile raf = new RandomAccessFile(file,"rw");
FileChannel channel = raf.getChannel();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE,0L,numCount * 4);
buffer.force();
channel.force(true);
channel.close();
raf.close();
unmap(buffer)

可以使用rename()方法測試,文件是否被占用

file.rename(file)

返回true重命名成功,返回false文件重命名失敗。

特殊情況

如果buffer的容量不夠用時,需要用channel.map()重新生成一個MappedByteBuffer對象,在映射新buffer之前應該先調用buffer.force()方法和unmap(buffer) 兩個方法。否則文件還是不能進行重命名和刪除操作。原因應該是原來存在堆中的buffer對象沒有被回收,而且還在持有文件的句柄。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM