内存映射文件

简介: 内存映射文件

基本思想

进程通过系统调用将一个文件或文件的部分映射到进程的虚拟地址空间的一部分,访问这个文件就像访问内存中的一个大数组,而不是对文件进行读写。

实现

在多数实现中,在影射共享的页面时不会涉及读入页面的内容,而是在访问页面时页面才会被每次一页读入,磁盘文件则被当做后备存储。

分类

内存映射文件主要有两个分类:持久化(Persisted)和非持久化(Non-persisted)

持久化

持久化文件时,当进程退出或显示地解除文件映射时,所有修改页面会写回映射的文件中。

非持久化

非持久化时,文件并不关联硬盘文件。当关闭内存映射文件时,所有数据被抛弃。非持久化适用于进程通信的共享内存。

在Windows操作系统中,不需要调用CreateFile。调用CreateFileMapping时,将INVALID_HANDLE_VALUE作为hFile参数传入,这表示创建的文件映射不是磁盘文件,而是页交换文件。

图例

Image.png

应用场景

大文件读写

如果是小文件,那么内存映射文件会导致碎片空间浪费,因为内存映射以页为单位加载数据,一般内存中一页通常至少要4KB。因此一个5KB的文件需要映射到内存需要两页,也就是8KB,从而浪费了3KB内存空间。

操作系统加载进程

多进程共享内存

Java对内存映射的支持

FileChannel
代码示例:

public class DiskHelper {
    private final String fileName;
    private final long fileSize;
    private FileChannel fileChannel;
    private MappedByteBuffer mappedByteBuffer;

    public DiskHelper(final String fileName, final long fileSize) throws IOException {
        this.fileName = fileName;
        this.fileSize = fileSize;

        // 内存映射
        File file = new File(fileName);
        try {
            this.fileChannel = new RandomAccessFile(file, "rw").getChannel();
            this.mappedByteBuffer = this.fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileChannel != null) {
                this.fileChannel.close();
            }

        }
    }

    public byte[] read(long offset, int length) {
        ByteBuffer buffer = mappedByteBuffer.slice();
        buffer.position((int) offset);
        byte[] bytes = new byte[length];
        buffer.get(bytes);
        buffer.clear();
        return bytes;
    }

    public void write(byte[] bytes, long offset) {
        ByteBuffer buffer = mappedByteBuffer.slice();
        buffer.position((int) offset);
        buffer.put(bytes);
        mappedByteBuffer.force();
    }

    public void write(byte[] bytes, int offset, int length) {
        ByteBuffer buffer = mappedByteBuffer.slice();
        buffer.position(offset);
        buffer.put(bytes, 0, length);
        mappedByteBuffer.force();
    }

    public void shutdown() {
        try {
            if (fileChannel != null) {
                fileChannel.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

测试用例

public class DiskHelperTest {

    @Test
    public void testCreateReadWrite() throws IOException {
        DiskHelper diskHelper = new DiskHelper("test", 1024L * 1024);
        String test = "abcd";
        diskHelper.write(test.getBytes(StandardCharsets.UTF_8), 0L);

        byte[] content = diskHelper.read(0, 4);
        System.out.println(new String(content, StandardCharsets.UTF_8));
        diskHelper.shutdown();
    }
}

参考

  1. https://zh.wikipedia.org/wiki/%E5%86%85%E5%AD%98%E6%98%A0%E5%B0%84%E6%96%87%E4%BB%B6
  2. 操作系统原理-内存映射文件 陈向群
目录
相关文章
|
7月前
|
存储 监控 API
7.7 实现进程内存读写
内存进程读写可以让我们访问其他进程的内存空间并读取或修改其中的数据。这种技术通常用于各种调试工具、进程监控工具和反作弊系统等场景。在`Windows`系统中,内存进程读写可以通过一些`API`函数来实现,如`OpenProcess`、`ReadProcessMemory`和`WriteProcessMemory`等。这些函数提供了一种通用的方式来访问其他进程的内存,并且可以用来读取或写入不同类型的数据,例如整数、字节集、浮点数等。在开始编写内存读者功能之前我们先来实现一个获取特定进程内特定模块基址的功能,该功能的实现分为两部分首先我们封装一个`GetProcessModuleHandle`函数
55 0
|
3月前
|
缓存 Linux 索引
内存学习(四):内存映射2
内存学习(四):内存映射2
43 0
|
3月前
|
Linux
内存学习(四):内存映射3
内存学习(四):内存映射3
31 0
|
3月前
|
存储 缓存 Linux
内存学习(四):内存映射1
内存学习(四):内存映射1
120 0
|
3月前
|
缓存 Linux
内存学习(四):内存映射5
内存学习(四):内存映射5
27 0
|
3月前
|
存储 缓存 Linux
内存学习(四):内存映射4
内存学习(四):内存映射4
27 0
使用内存映射提高BufferedRandoAccessFile性能(测试可用)
使用内存映射提高BufferedRandoAccessFile性能(测试可用)
98 0
|
存储 缓存 Java
mmap内存映射原理
mmap内存映射原理
172 0
|
存储 消息中间件 缓存
存储文件内存映射-TransientStorePool|学习笔记
快速学习存储文件内存映射-TransientStorePool
155 0
|
存储 消息中间件 缓存
存储文件内存映射-MappedFile|学习笔记
快速学习存储文件内存映射-MappedFile
203 0
存储文件内存映射-MappedFile|学习笔记