当前位置: 首页 > 面试题库 >

Windows:移动先前在内存中映射的文件失败

楚嘉玉
2023-03-14
问题内容

在将代码从Linux(Ubuntu LTS 12.4)环境移植到Windows Server 2008的过程中,我遇到一个问题。

我需要使用内存映射文件,但无法在Windows上防止以下错误。

在下面的单元测试中重现了此问题。这两个测试在Linux上均成功完成,但是在Windows上,测试 testWithRandowmAccessFile
失败,并且堆栈跟踪位于底部。 testWithRandowmAccessFile 测试失败

的根本原因是什么? 我应该如何在Windows上实现它? ****

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import org.apache.commons.io.IOUtils;
import org.junit.Test;

public class TestIOOnWindows {

    @Test
    public void testWithRandowmAccessFile() throws IOException {
        final File sourceFile = new File("source.txt");
        final File manipulatedFile = new File("manipulated.txt");
        final File targetFile = new File("target.txt");

        try
        (
            FileInputStream sourceInputStream = new FileInputStream(sourceFile);
            RandomAccessFile manipulated = new RandomAccessFile(manipulatedFile, "rw");
            FileChannel fcOut = manipulated.getChannel()
        ) 
        {
            byte[] sourceBytes = new byte[Long.valueOf(sourceFile.length()).intValue()];
            IOUtils.read(sourceInputStream, sourceBytes);

            final int length = sourceBytes.length;

            // ========= with this single line on Windows, the move fails ======
            MappedByteBuffer byteBuffer = fcOut.map(FileChannel.MapMode.READ_WRITE, 0, length);
            // commenting this line would not prevent the error on Windows
            byteBuffer.put(sourceBytes, 0, length);            
        }

        Files.move(
                Paths.get(manipulatedFile.getAbsolutePath()),
                Paths.get(targetFile.getAbsolutePath()),
                StandardCopyOption.REPLACE_EXISTING);
    }

    @Test
    public void testWithFileOutputStream() throws IOException {
        final File sourceFile = new File("source.txt");
        final File manipulatedFile = new File("manipulated.txt");
        final File targetFile = new File("target.txt");

        try
        (
            FileInputStream sourceInputStream = new FileInputStream(sourceFile);
            FileOutputStream manipulatedOutputStream = new FileOutputStream(manipulatedFile);
            FileChannel fcIn = sourceInputStream.getChannel();
            FileChannel fcOut = manipulatedOutputStream.getChannel()
        ) 
        {
            final long length = sourceFile.length();

            // ========= with this single line on Windows, the move succeed ====
            fcIn.transferTo(0, length, fcOut);
        }

        Files.move(
                Paths.get(manipulatedFile.getAbsolutePath()),
                Paths.get(targetFile.getAbsolutePath()),
                StandardCopyOption.REPLACE_EXISTING);
    }
}

添加在Windows上从命令提示符运行单元测试时得到的跟踪跟踪。

There was 1 failure:
1) testWithRandowmAccessFile(TestIOOnWindows) java.nio.file.FileSystemException: C:\Users\Administrator\AppData\Local\Temp\manipulated.txt -> C:\Users\Administrator\AppData\Local\Temp\target.txt: The process cannot access the file because it is being used by another process.

    at sun.nio.fs.WindowsException.translateToIOException(Unknown Source)
    at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
    at sun.nio.fs.WindowsFileCopy.move(Unknown Source)
    at sun.nio.fs.WindowsFileSystemProvider.move(Unknown Source)
    at java.nio.file.Files.move(Unknown Source)
    ===> at TestIOOnWindows.testWithRandowmAccessFile(TestIOOnWindows.java:40) <===
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.junit.runners.Suite.runChild(Suite.java:128)
    at org.junit.runners.Suite.runChild(Suite.java:24)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:136)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:117)
    at org.junit.runner.JUnitCore.runMain(JUnitCore.java:98)
    at org.junit.runner.JUnitCore.runMainAndExit(JUnitCore.java:53)
    at org.junit.runner.JUnitCore.main(JUnitCore.java:45)

FAILURES!!!
Tests run: 2,  Failures: 1

问题答案:

在Java中,文件映射是垃圾收集的,并且没有支持的方法来强制销毁映射。

FileChannel.map()文档中:

缓冲区及其表示的映射将保持有效,直到缓冲区本身被垃圾回收为止。

映射一旦建立,就不依赖于用于创建它的文件通道。特别是,关闭通道对映射的有效性没有影响。

在Sun的JDK中,可以通过在执行文件移动之前强行破坏映射来测试这确实是罪魁祸首:

import sun.nio.ch.DirectBuffer;
import sun.misc.Cleaner;
[...]
if (byteBuffer.isDirect()) {
    Cleaner cleaner = ((DirectBuffer) byteBuffer).cleaner();
    cleaner.clean();
}
// move file


 类似资料:
  • 问题内容: 我一直在尝试编写一些非常快速的Java代码,这些代码必须执行很多I / O。我正在使用返回ByteBuffer的内存映射文件: 我遇到的问题是ByteBuffer .array()方法(应返回一个byte []数组)不适用于只读文件。我想编写我的代码,以便它可以与构造在内存中的内存缓冲区和从磁盘读取的缓冲区一起使用。但是我不想将所有缓冲区都包装为ByteBuffer.wrap()函数,

  • 读 # mmap_read.py import mmap with open('lorem.txt', 'r') as f: with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m: print('First 10 bytes via read :', m.read(10

  • 问题内容: 最近,我碰到了这篇文章,这篇文章很好地介绍了内存映射文件以及如何在两个进程之间共享它。这是读取文件的过程的代码: 但是,我对这种方法有几点评论/问题: 如果我们仅对空文件执行读取器,即运行 这将分配8000个字节,现在将扩展文件。返回的缓冲区的限制为8000,位置为0,因此,读取器可以继续读取空数据。发生这种情况后,阅读器将停止,如。 现在应该是作家了(代码被省去了,因为它很简单,可以

  • 问题 你想内存映射一个二进制文件到一个可变字节数组中,目的可能是为了随机访问它的内容或者是原地做些修改。 解决方案 使用 mmap 模块来内存映射文件。 下面是一个工具函数,向你演示了如何打开一个文件并以一种便捷方式内存映射这个文件。 import os import mmap def memory_map(filename, access=mmap.ACCESS_WRITE): siz

  • 我想写一个字节向量,

  • 系统调用在调用进程的虚拟地址空间中提供映射,将文件或设备映射到内存中。 下面是两种类型 - 文件映射或文件支持的映射 - 此映射将进程的虚拟内存区域映射到文件。 这意味着读取或写入这些内存区域会导致文件被读取或写入。这是默认的映射类型。 匿名映射 - 此映射映射进程的虚拟内存区域,不受任何文件的支持。 内容被初始化为零。 这种映射类似于动态内存分配(malloc()),在某些实现中用于某些分配。