如何正确关闭MappedByteBuffer?

Zhe*_*lov 5 java randomaccessfile mappedbytebuffer java-9

这是我正在运行的代码:

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class Main {
    public static void main(String[] args) throws Exception {
        String filePath = "D:/temp/file";
        RandomAccessFile file = new RandomAccessFile(filePath, "rw");

        try {
            MappedByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 128);

            // Do something
            buffer.putInt(4);
        } finally {
            file.close();
            System.out.println("File closed");
        }

        System.out.println("Press any key...");
        System.in.read();

        System.out.println("Finished");
    }
}
Run Code Online (Sandbox Code Playgroud)

在按下某个键之前,我正在尝试在FAR Manager中手动删除该文件.但FAR说该文件被锁定:

 The process cannot access the file because it is being used by another process.
                     Cannot delete the file
                         D:\temp\file
                    Object is being opened in:
 Java(TM) Platform SE binary (PID: 5768, C:\Program Files\Java\jdk1.8.0_05\bin\javaw.exe)
Run Code Online (Sandbox Code Playgroud)

只有在按下一个键后,应用程序才会终止,我可以删除该文件.

我的代码出了什么问题?

Zhe*_*lov 8

@SANN3 的答案不再适用于 Java 9。在Java 9中有一个sun.misc.Unsafe.invokeCleaner可以使用的新方法。这是一个工作代码:

MappedByteBuffer buffer = ...

// Java 9+ only:
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Object unsafe = unsafeField.get(null);
Method invokeCleaner = unsafeClass.getMethod("invokeCleaner", ByteBuffer.class);
invokeCleaner.invoke(unsafe, buffer);
Run Code Online (Sandbox Code Playgroud)

  • 这确实有效,但是必须有比使用反射更好的方法。对我来说听起来像是一个 Java 错误/缺陷。 (2认同)
  • 工程师找到了实际的错误:https://bugs.openjdk.java.net/browse/JDK-4724038 - 它实际上被标记为增强。引用该错误:_关于 JDC 注释中描述的“解决方法”,(ab)使用反射来调用映射缓冲区的清理对象:温和地说,这是非常不可取的。强制取消映射对 Java 代码可见的映射字节缓冲区是极其危险的。这样做会危及系统的安全性和稳定性。_ (2认同)

SAN*_*NN3 5

试试这个吧.

public class Test
{
    public static void main(String[] args) throws Exception {
        String filePath = "D:/temp/file";
        RandomAccessFile file = new RandomAccessFile(filePath, "rw");
        FileChannel chan = file.getChannel();
        try {
            MappedByteBuffer buffer = chan.map(FileChannel.MapMode.READ_WRITE, 0, 128);

            // Do something
            buffer.putInt(4);
            buffer.force();
            Cleaner cleaner = ((sun.nio.ch.DirectBuffer) buffer).cleaner();
            if (cleaner != null) {
                cleaner.clean();
            }
        } finally {
            chan.close();
            file.close();
            System.out.println("File closed");
        }

        System.out.println("Press any key...");
        System.in.read();

        System.out.println("Finished");
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我猜这个问题是由[这个bug](http://bugs.java.com/view_bug.do?bug_id=4715154)引起的:只要映射的缓冲区没有被垃圾收集,它就会阻止文件被删除. 我猜想清洁器只是做与垃圾收集期间会发生的事情相同的事情...... (2认同)
  • DirectBuffer不是内部API吗?它刚刚开始向我展示这样的错误。 (2认同)