Java文件映射[mmap]揭秘

前言

相信现在做Java的人没有人不用NIO来进行IO相关的操作了吧。这个新的IO类库[虽然现在已经不新了]为我们带来了基于块的IO处理方式,通过预定义的Buffer,我们可以更高效地完成IO操作。在NIO中,我比较关注的是一个成为mmap的文件映射功能,其特点是可以把文件的一部分或全部映射到内存中,之后我们就可以通过MappedBuffer对内存进行操作,而操作的结果会由操作系统负责flush到文件中。由于应用程序只是操作内存,所以处理速度比普通的文件操作快很多,在某些应用场景下mmap可以发挥相当大的作用。本文就来揭秘java的mmap背后的工作原理和实现方法,以及使用java的mmap要注意的一些问题。

1功能简析

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

(1)RandomAccessFile raf = new RandomAccessFile (File, "rw");

(2)FileChannel channel = raf.getChannel();

(3)MappedByteBuffer buff = channel.map(FileChannel.MapMode.READ_WRITE,startAddr,SIZE);

(4)buf.put((byte)255);

(5)buf.write(byte[] data)

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

虽然mmap功能是如此的强大,但凡事都有局限,java的mmap瓶颈在哪里?使用mmap会遇到哪些问题和限制?要回到这些问题,还是需要先从mmap的实现入手。

2实现原理

研究实现原理的最好方式就是阅读源码,由于SUN(或许不应该这样叫了?)开放了JDK源码,为我们的研究敞开了大门,这里我采用的是linux版的JDK1.6_u13的源码。

2.1目标和方法

在查看Java源码之前,我首先google了一下mmap,结果发现mmap在linux下是一个系统调用:

void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off );

man了一下发现其功能描述和JavaAPI上说的差不多,难道JDK底层就是用这个东东实现的?马上动手写个程序然后STrace一下看看是不是使用了这个系统调用。这个测试程序应用的就是上面提到的那个程序框架,map了1G的文件,然后每次一个字节地往里面写数据,由于很简单这里就不贴出来了。结果如下:

为简便起见中间的内容就忽略掉了,不过我们可以很清楚地看到mmap的操作就是打开[使用open系统调用]文件,然后mmap之,之后的操作都是对内存地址的直接操作,而操作系统负责把剩下的事情搞定了。于是可以大胆预言,java的实现是用JNI包装了的mmap()系统调用。其功能也应该和下图所示的内容保持一致。

《APUE》中关于Mmap()系统调用的示意图

在经过上面的分析之后,我们已经有了初步的目标,那就是找到JavaMmap的C源码,看其使用了哪些系统调用。这样我们就可以更好地了解和控制JavaMmap的行为。

2.2询源之旅

还是以上面这个代码框架为例,注意这里除了map文件的动作之外就只有写操作,因为mmap的读方法是读内存的,我们已经很清楚,所以这里我们只关心写操作。通过阅读源码,我得到的结论如下:

(1)打开文件和建立FileChannel这两步应该只有一个open()系统调用。

(2)mmap方法没什么悬念地用到了mmap系统调用。但值得注意的是JDK只提供了建立文件/内存映射的方法,而没有给出解除映射关系的API。在FileChannelImpl.java中我们可以看到,解除映射的方法[在Unmapper中定义]是在创建MappedByteBuffer时嵌入到这个类里面的,在buffer被GC回收之前会调用Unmapper的unmap方法来解除文件到内存的映射关系。也就是说我们要想解除映射只能先把buffer置为null,然后祈祷GC赶紧起作用,实在等不及还可以用System.gc()催促一下GC赶快干活,不过后果是会引发FullGC。

(3)对于map到内存中的部分的写操作就是对内存地址的写操作,只不过jdk用的是jni。

3 诡异的问题

因为在一般运维监控的时候,我们都会很自然地选择Top或者PS看一下进程当前实用的物理内存是多少,以防进程内存占用过高导致系统崩溃。虽然TOP/PS的结果不是十分精确,但是大部分时候还是够用的。然而在使用了java的mmap之后我们发现,top

mmap java_Java文件映射[Mmap]揭秘 | 学步园相关推荐

  1. linux mmap内存文件映射

    一.传统文件访问 unix访问文件的传统方法使用open打开他们,如果有多个进程访问一个文件,则每一个进程在再记得地址空间都包含有该文件的副本,这不必要地浪费了存储空间.下面说明了两个进程同时读一个文 ...

  2. 使用mmap实现文件映射

    1 文件映射 传统文件访问方式是, 首先用open系统调用打开文件, 然后使用read, write以及lseek等调用进行顺序或者随即的I/O. 这种方式是非常低效的, 每一次I/O操作都需要一次系 ...

  3. bfo java_Java操作PDF文件(BFO) | 学步园

    上一次我们用iText这个工具在Java环境下操作PDF文件,现在我们换一个工具:BFO iText的确小巧,但是功能也有限制,只能简单的生成PDF文件,BFO却不同,能设置字体.版面等元素. 最新版 ...

  4. python如何调用文件进行换位加密_数据文件加密算法–换位加密法 | 学步园

    #include #include union message{ int s1[1000]; int s2[100][10]; } user; main(int argc,char *argv[]) ...

  5. python遍历文件夹下所有文件大小_python遍历文件夹读取文件大小 | 学步园

    闲来无事,写了个小程序删除内存卡中大于50m的文件 # filename  itertaorfilefolder import os import os.path filePath = raw_inp ...

  6. java 调用cpp_java jni 调用cpp文件中的方法 | 学步园

    这里说下最近修改nutch的源码碰到的调用cpp文件中的方法. 刚刚开始的时候,想直接在fetch.java中直接写jni接口.后来发现这样会留下不好的编程习惯.自己动手在fetch下写一个类.Hel ...

  7. python以写模式打开录入_Python的学习(六)—-文件和输入输出处理 | 学步园

    Python中的文件和输入输出处理 1. 文件对象的概念 文件对象不仅可以用来访问普通的磁盘文件,还可以访问任何其他类型抽象层面上的文件. 例如实时地"打开一个URL"来读取Web ...

  8. java 写入指定路径文件_java对指定目录下文件读写操作介绍 | 学步园

    最近因为项目的国际化的需要,需要对整个项目的100来个插件做国际化,这是一件痛苦的事情,因为纯体力劳动.为了省点工作量,想着能不能写个程序批处理了,减少点工作量,于是就有了下面的代码. 1.读取指定的 ...

  9. runtime批处理mysql导出_【原】使用批处理BAT文件处理Mysql数据库 | 学步园

    在做项目的时候,考虑到项目中很多模块是公用,数据库也是公用,所以考虑把公用模块的数据库全部用批处理生产,这样或多或少提高了一些效率. 处理方法是: 1:用一个txt保存该项目数据库名称,方便新建工程时 ...

  10. matlab保存数据用什么指令_MATLAB文件操作及保存文件save load fopen | 学步园

    一.保存文件 1.保存整个工作区 File->Save Workspace as...一个.mat文件 2.保存工作区的变量 在左工作区右击变量名,create M-File 3.save命令 ...

最新文章

  1. ScrollView充满屏幕
  2. 也谈Hashtable
  3. 【设计模式】加薪非要老总批 --- 职责链模式
  4. 图像处理与图像识别笔记(六)图像增强3
  5. 网页中、英文安全字体选择及设置
  6. db2取数据库日期时间_DB2数据库取得当前时间的正确解析
  7. jsp mysql电子档案管理系统_学生档案管理系统的设计与实现(JSP,MySQL)(含录像)
  8. 实现java内存队列消费事件-ConcurrentLinkedQueue
  9. MYSQL建表操作大全
  10. leetcode算法88.合并两个有序数组
  11. vivado SRIO 学习
  12. L-ScanPort2.0beta版+完整源代码
  13. 经典贪心算法问题:会议安排
  14. Wireshark菜单栏介绍
  15. 光伏运维将面临行业洗牌?
  16. 【OptiX】第6个示例 折射,玻璃材质
  17. 如何用计算机制作统计图,如何在EXCEL里做条形统计图?
  18. 关于switch的参数类型
  19. Hive的Parquet存储与LZO压缩
  20. unity获取物体下的所有子物体

热门文章

  1. 77款免费可商用字体!!!
  2. JPA JAVA代码生成器(entity ,controller ,service ,repository)
  3. 电脑必备必装的软件工具神器,强烈推荐
  4. java 发送支持超链接的邮件
  5. 【MATLAB】求定积分
  6. Altium Designer元件库下载
  7. 人民币转换美金的c语言代码大全,C语言 人民币转换代码
  8. Axure8.0如何汉化?
  9. matlab离散信号与系统的时域分析
  10. 手把手入门三菱PLC FX2N系列(一)安装GX works2 、 连接PLC、基本操作