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对象没有被回收,而且还在持有文件的句柄。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供一个示例代码,将Java的FileChannel和MappedByteBuffer用于读写文件,并将读和写的代码分开。请参考以下示例: 首先,我们来看一下读取文件的代码: ```java import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class FileReader { public static void main(String[] args) { try { // 打开文件 RandomAccessFile file = new RandomAccessFile("input.txt", "r"); FileChannel channel = file.getChannel(); // 将文件映射到内存中 MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); // 读取文件内容 while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); } // 关闭文件 channel.close(); file.close(); } catch (Exception e) { e.printStackTrace(); } } } ``` 接下来,我们来看一下写入文件的代码: ```java import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class FileWriter { public static void main(String[] args) { try { // 打开文件 RandomAccessFile file = new RandomAccessFile("output.txt", "rw"); FileChannel channel = file.getChannel(); // 将文件映射到内存中 MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 1024); // 写入数据到缓冲区 String data = "Hello, World!"; buffer.put(data.getBytes()); // 刷新缓冲区到文件 buffer.force(); // 关闭文件 channel.close(); file.close(); } catch (Exception e) { e.printStackTrace(); } } } ``` 这两个示例代码分别用于读取和写入文件。您可以根据自己的需求进行修改和扩展。记得替换文件名和路径以适应您的实际情况。希望对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值