如何正确关闭 MappedByteBuffer?
Posted
技术标签:
【中文标题】如何正确关闭 MappedByteBuffer?【英文标题】:How to properly close MappedByteBuffer? 【发布时间】:2014-08-11 07:42:37 【问题描述】:这是我正在运行的代码:
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");
在按键之前,我尝试在 FAR 管理器中手动删除文件。但 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)
只有在按键后,应用程序才会终止,我可以删除文件。
我的代码有什么问题?
【问题讨论】:
似乎对我有用。您是否尝试过查找任何正在运行的 Java 进程并通过在 Windows 上按 shift+escape 来关闭它们/关闭您可能打开并再次运行它的所有文件实例。 没有帮助。我可以选择任意文件(保证是新的),例如D:/temp/file2441,仍然面临同样的问题。 【参考方案1】:试试这个。
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");
【讨论】:
我猜这个问题是this bug引起的:只要映射的缓冲区没有被垃圾回收,就会阻止文件被删除。我猜 Cleaner 所做的事情与垃圾收集期间发生的事情一样...... DirectBuffer 不是内部 API 吗?它刚刚开始向我显示这样的错误。【参考方案2】:@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);
【讨论】:
【参考方案3】:如果你使用的是java1.8,不能直接使用sun.nio.ch.DirectBuffer和Cleaner,可以试试:
public void clean(final ByteBuffer buffer)
AccessController.doPrivileged((PrivilegedAction<Object>) () ->
try
Field field = buffer.getClass().getDeclaredField("cleaner");
field.setAccessible(true);
Object cleaner = field.get(buffer);
Method cleanMethod = cleaner.getClass().getMethod("clean");
cleanMethod.invoke(cleaner);
catch (Exception e)
e.printStackTrace();
return null;
);
【讨论】:
以上是关于如何正确关闭 MappedByteBuffer?的主要内容,如果未能解决你的问题,请参考以下文章
使用 MappedByteBuffer 时出现 IndexOutOfBoundsException
如何防止 MappedByteBuffer 在文件末尾写入空字符?