慎用 MappedByteBuffer!

阅读更多

最近使用MD5进行大文件验证,固使用NIO这种高效率的模式来进行文件映射:

FileInputStream in = new FileInputStream(file);
FileChannel ch = in.getChannel();
MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
messageDigest.update(byteBuffer);
String md5 = bufferToHex(messageDigest.digest());
ch.close();
in.close();

本来想如果文件md5与数据库存储的值不同就删掉该文件的,结果出现了文件无法删除的情况。

抛出的违例
java.io.FileNotFoundException: E:\hello.jar (请求的操作无法在使用用户映射区域打开的文件上执行。)

后来经查,原来是当使用 FileChannel.map 方法时,MappedByteBuffer 已经在系统内占用了一个句柄,而使用 FileChannel.close 方法是无法释放这个句柄的,且FileChannel有没有提供类似 unmap 的方法,因此会出现无法删除文件的情况。

 

此问题一直让我很郁闷,后来到网上查询到两种方法...

 

第一种:

AccessController.doPrivileged(new PrivilegedAction() {
  public Object run() {
    try {
      Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
      getCleanerMethod.setAccessible(true);
      sun.misc.Cleaner cleaner = (sun.misc.Cleaner) 
      getCleanerMethod.invoke(byteBuffer, new Object[0]);
      cleaner.clean();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }
});

此种方法需要JDK支持,我用的是JRE 1.6,提示没有 sun.misc.Cleaner 类,JDK包太大,项目又不让用。

 

第二种方法是显性设置byteBuffer为null,并调用GC,没什么实际意义。

 

实在没招了,又回来使用InputStream了...

FileInputStream in = new FileInputStream(file);
ByteArrayOutputStream out = new ByteArrayOutputStream((int)file.length());
byte[] cache = new byte[1048576];
for(int i = in.read(cache);i != -1;i = in.read(cache)){
  out.write(cache, 0, i);
}
in.close();
out.close();
messageDigest.update(out.toByteArray());
md5 = bufferToHex(messageDigest.digest());

不知道SUN为啥要搞这种半截子工程,大家有什么其他解决方法么?

你可能感兴趣的:(JVM,SUN,Cache,Linux,设计模式)