釋放 MappedByteBuffer映射的內存


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


免責聲明!

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



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