Java mmap

Java的mmap系统调用允许用户程序直接访问设备内存,提高效率。它创建虚拟映射区域,通过内核的mmap函数实现文件到内存的映射。与普通文件操作相比,mmap减少了一次数据拷贝,提供进程间共享内存,适用于大文件读取和高效数据传输。mmap与共享内存的主要区别在于是否涉及文件IO。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mmap系统调用(内核空间到用户空间的映射)

mmap操作提供了一种机制,让用户程序直接访问设备内存,这种机制,相比较在用户空间和内核空间互相拷贝数据,效率更高。在要求高性能的应用中比较常用。mmap映射内存必须是页面大小的整数倍,面向流的设备不能进行mmap,mmap的实现和硬件有关。

作为NIO的一个重要的功能,Mmap方法为我们提供了将文件的部分或全部映射到内存地址空间的能力,同当这块内存区域被写入数据之后[dirty],操作系统会用一定的算法把这些数据写入到文件中[这一过程java并没有提供API,后面会提到]。这样我们实际上就获得了间接操纵内存的能力,而且内存与文件之间的同步是由操作系统完成的,不用我们额外操心。也就是说,只要我们把内存数据块规划好[也就是实现一下C语言的SharedMemory功能],剩下的事情交给操作系统烦恼就好了。我们既获得了高效的读写操作能力,又解决了数据的持久化问题,多么理想的功能啊!但必须说明的是mmap毕竟不是数据库,不能很方便地提供事务功能、类似sql语句那样的查找功能,也不具备备份、回滚、迁移的能力,这些都要自己实现。不过这样显然不如放在数据库里放心,所以我们的经验是特别重要的数据还是存数据库,不太重要的、但是又访问量很大、读写操作多且需要持久化功能的数据是最适合使用mmap功能的。
这里写图片描述


import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/* 
 *
 * @since 2016年6月27日
 * @author wwhhff11
 * @comment 读取大文件
 */
public class ReadFile {

    public static String PATH = "G:\\文件\\大数据实训\\purchase\\数据处理\\分类之间的权重.csv";
    public static int num = 8;
    public static int BUFFER_SIZE = 0x200000;// 缓冲区大小为3M

    /* 
     *
     * @since 2016年6月27日
     * @author wwhhff11
     * @comment mmap
     */
    public static void mmap() throws Exception {
        File file = new File(PATH);
        RandomAccessFile randomAccessFile = new RandomAccessFile(PATH, "r");
        MappedByteBuffer buff = null;
        long file_size = file.length();
        long rows = file_size / num;
        int total = num + (file_size % num == 0 ? 0 : 1);
        long start_pos = 0l;
        long size = 0l;
        byte[] dst = null;
        for (int i = 1; i <= total; i++) {
            start_pos = (i - 1) * rows;
            size = (i == total ? file_size - start_pos : rows);
            buff = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, start_pos, size);
            dst = new byte[BUFFER_SIZE];
            // 每次读出3M的内容
            for (int offset = 0; offset < buff.capacity(); offset += BUFFER_SIZE) {
                if (buff.capacity() - offset >= BUFFER_SIZE) {
                    // 剩余文件大小大于等于3M
                    for (int j = 0; j < BUFFER_SIZE; j++)
                        dst[j] = buff.get(offset + j);
                } else {
                    // 剩余文件大小小于3M
                    for (int j = 0; j < buff.capacity() - offset; j++)
                        dst[j] = buff.get(offset + j);
                }
                int length = (buff.capacity() % BUFFER_SIZE == 0) ? BUFFER_SIZE : buff.capacity() % BUFFER_SIZE;
                System.out.println(new String(dst, 0, length));
            }
        }

    }

    /* 
     *
     * @since 2016年6月27日
     * @author wwhhff11
     * @comment 按行读取
     */
    public static void randomRead() throws IOException {
        FileInputStream fis = new FileInputStream(PATH);
        RandomAccessFile raf = new RandomAccessFile(new File(PATH), "r");
        String s;
        while ((s = raf.readLine()) != null) {
            System.out.println(s);
        }
        raf.close();
        fis.close();
    }

    public static void main(String[] args) throws Exception {
//      mmap();
        randomRead();
    }

}

其中最重要的就是那个buff,它是文件在内存中映射的标的物,通过对buff的read/write我们就可以间接实现对于文件的读写操作,当然写操作是操作系统帮忙完成的。

Mmap和共享内存[SharedMemory]有何异同?

mmap似乎和共享内存[后面简称SM]有很多相同之处。相同点是共享的概念,mmap和SM都可以让操作系统划分出一块内存来共多个进程共享使用,也就是说多个进程可以对同一块内存进行读写操作,某一个进程的写入操作的结果可以被其他进程看到。不同点是mmap需要把内容写回到文件,所以还需要与文件打交道;而SM则是完全的内存操作,不涉及文件IO,效率上可能会好很多。还有就是SM使用的系统调用是shmget和shmctl。普通的JDK并没有提供SM的几口,而收费的JavaRTS提供了SM接口,也是通过JNI实现的。
值得注意的是,无论是mmap还是SM,他们初始所向操作系统申请的那一块内存并不是一开始就能全额得到,操作系统会根据当前的内存使用状况为期分配一定大小的内存,而如果系统内存不足的话,操作系统还可以选择把mmap或SM中不常用的内存页换出来腾出地方给其他进程用。

原理:

mmap内存映射的实现过程,总的来说可以分为三个阶段:
(一)进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域
(二)调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系
(三)进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝
注:修改过的脏页面并不会立即更新回文件中,而是有一段时间的延迟,可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了

与普通文件操作的区别:

总而言之,常规文件操作需要从磁盘到页缓存再到用户主存的两次数据拷贝。而mmap操控文件,只需要从磁盘到用户主存的一次数据拷贝过程

优点

1、对文件的读取操作跨过了页缓存,减少了数据的拷贝次数,用内存读写取代I/O读写,提高了文件读取效率。
2、实现了用户空间和内核空间的高效交互方式。两空间的各自修改操作可以直接反映在映射的区域内,从而被对方空间及时捕捉。
3、提供进程间共享内存及相互通信的方式。不管是父子进程还是无亲缘关系的进程,都可以将自身用户空间映射到同一个文件或匿名映射到同一片区域。从而通过各自对映射区域的改动,达到进程间通信和进程间共享的目的。
4、可用于实现高效的大规模数据传输。内存空间不足,是制约大数据操作的一个方面,解决方案往往是借助硬盘空间协助操作,补充内存的不足。但是进一步会造成大量的文件I/O操作,极大影响效率。这个问题可以通过mmap映射很好的解决。换句话说,但凡是需要用磁盘空间代替内存的时候,mmap都可以发挥其功效

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值