林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka

摘要:本文主要讲了java中内存映射的原理及过程,与传统IO进行了对比,最后,用实例说明了结果。

一、java中的内存映射IO和内存映射文件是什么?

内存映射文件非常特别,它允许Java程序直接从内存中读取文件内容,通过将整个或部分文件映射到内存,由操作系统来处理加载请求和写入文件,应用只需要和内存打交道,这使得IO操作非常快。加载内存映射文件所使用的内存在Java堆区之外。Java编程语言支持内存映射文件,通过java.nio包和MappedByteBuffer 可以从内存直接读写文件。

内存映射文件  

内存映射文件,是由一个文件到一块内存的映射。Win32提供了允许应用程序把文件映射到一个进程的函数 (CreateFileMapping)。内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而且在对该文件进行操作之前必须首先对文件进行映射。使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。

内存映射IO

在传统的文件IO操作中,我们都是调用操作系统提供的底层标准IO系统调用函数 read()、write() ,此时调用此函数的进程(在JAVA中即java进程)由当前的用户态切换到内核态,然后OS的内核代码负责将相应的文件数据读取到内核的IO缓冲区,然 后再把数据从内核IO缓冲区拷贝到进程的私有地址空间中去,这样便完成了一次IO操作。这么做是为了减少磁盘的IO操作,为了提高性能而考虑的,因为我们的程序访问一般都带有局部性,也就是所 谓的局部性原理,在这里主要是指的空间局部性,即我们访问了文件的某一段数据,那么接下去很可能还会访问接下去的一段数据,由于磁盘IO操作的速度比直接 访问内存慢了好几个数量级,所以OS根据局部性原理会在一次 read()系统调用过程中预读更多的文件数据缓存在内核IO缓冲区中,当继续访问的文件数据在缓冲区中时便直接拷贝数据到进程私有空间,避免了再次的低 效率磁盘IO操作。其过程如下

内存映射文件和之前说的 标准IO操作最大的不同之处就在于它虽然最终也是要从磁盘读取数据,但是它并不需要将数据读取到OS内核缓冲区,而是直接将进程的用户私有地址空间中的一 部分区域与文件对象建立起映射关系,就好像直接从内存中读、写文件一样,速度当然快了。

内存映射的优缺点

内存映射IO最大的优点可能在于性能,这对于建立高频电子交易系统尤其重要。内存映射文件通常比标准通过正常IO访问文件要快。另一个巨大的优势是内存映 射IO允许加载不能直接访问的潜在巨大文件 。经验表明,内存映射IO在大文件处理方面性能更加优异。尽管它也有不足——增加了页面错误的数目。由于操作系统只将一部分文件加载到内存,如果一个请求 页面没有在内存中,它将导致页面错误。同样它可以被用来在两个进程中共享数据。

支持内存映射IO的操作系统

大多数主流操作系统比如Windows平台,UNIX,Solaris和其他类UNIX操作系统都支持内存映射IO和64位架构,你几乎可以将所有文件映射到内存并通过JAVA编程语言直接访问。

Java的内存映射IO的要点

如下为一些你需要了解的java内存映射要点:

java通过java.nio包来支持内存映射IO。
内存映射文件主要用于性能敏感的应用,例如高频电子交易平台。
通过使用内存映射IO,你可以将大文件加载到内存。
内存映射文件可能导致页面请求错误,如果请求页面不在内存中的话。
映射文件区域的能力取决于于内存寻址的大小。在32位机器中,你不能访问超过4GB或2 ^ 32(以上的文件)。
内存映射IO比起Java中的IO流要快的多。
加载文件所使用的内存是Java堆区之外,并驻留共享内存,允许两个不同进程共享文件。
内存映射文件读写由操作系统完成,所以即使在将内容写入内存后java程序崩溃了,它将仍然会将它写入文件直到操作系统恢复。
出于性能考虑,推荐使用直接字节缓冲而不是非直接缓冲。
不要频繁调用MappedByteBuffer.force()方法,这个方法意味着强制操作系统将内存中的内容写入磁盘,所以如果你每次写入内存映射文件都调用force()方法,你将不会体会到使用映射字节缓冲的好处,相反,它(的性能)将类似于磁盘IO的性能。

万一发生了电源故障或主机故障,将会有很小的机率发生内存映射文件没有写入到磁盘,这意味着你可能会丢失关键数据。

二、实例代码

1、传统IO读取数据,不指定缓冲区大小

  1. /**
  2. * 传统IO读取数据,不指定缓冲区大小
  3. * @author linbingwen
  4. * @since  2015年9月5日
  5. * @param path
  6. * @return
  7. */
  8. public static void readFile1(String path) {
  9. long start = System.currentTimeMillis();//开始时间
  10. File file = new File(path);
  11. if (file.isFile()) {
  12. BufferedReader bufferedReader = null;
  13. FileReader fileReader = null;
  14. try {
  15. fileReader = new FileReader(file);
  16. bufferedReader = new BufferedReader(fileReader);
  17. String line = bufferedReader.readLine();
  18. System.out.println("========================== 传统IO读取数据,使用虚拟机堆内存 ==========================");
  19. while (line != null) { //按行读数据
  20. System.out.println(line);
  21. line = bufferedReader.readLine();
  22. }
  23. } catch (FileNotFoundException e) {
  24. e.printStackTrace();
  25. } catch (IOException e) {
  26. e.printStackTrace();
  27. } finally {
  28. //最后一定要关闭
  29. try {
  30. fileReader.close();
  31. bufferedReader.close();
  32. } catch (IOException e) {
  33. e.printStackTrace();
  34. }
  35. long end = System.currentTimeMillis();//结束时间
  36. System.out.println("传统IO读取数据,不指定缓冲区大小,总共耗时:"+(end - start)+"ms");
  37. }
  38. }
  39. }

2、传统IO读取数据,指定缓冲区大小

  1. /**
  2. * 传统IO读取数据,指定缓冲区大小
  3. * @author linbingwen
  4. * @since  2015年9月5日
  5. * @param path
  6. * @return
  7. * @throws FileNotFoundException
  8. */
  9. public static void readFile2(String path) throws FileNotFoundException {
  10. long start = System.currentTimeMillis();//开始时间
  11. int bufSize = 1024 * 1024 * 5;//5M缓冲区
  12. File fin = new File(path); // 文件大小200M
  13. FileChannel fcin = new RandomAccessFile(fin, "r").getChannel();
  14. ByteBuffer rBuffer = ByteBuffer.allocate(bufSize);
  15. String enterStr = "\n";
  16. long len = 0L;
  17. try {
  18. byte[] bs = new byte[bufSize];
  19. String tempString = null;
  20. while (fcin.read(rBuffer) != -1) {//每次读5M到缓冲区
  21. int rSize = rBuffer.position();
  22. rBuffer.rewind();
  23. rBuffer.get(bs);//将缓冲区数据读到数组中
  24. rBuffer.clear();//清除缓冲
  25. tempString = new String(bs, 0, rSize);
  26. int fromIndex = 0;//缓冲区起始
  27. int endIndex = 0;//缓冲区结束
  28. //按行读缓冲区数据
  29. while ((endIndex = tempString.indexOf(enterStr, fromIndex)) != -1) {
  30. String line = tempString.substring(fromIndex, endIndex);//转换一行
  31. System.out.print(line);
  32. fromIndex = endIndex + 1;
  33. }
  34. }
  35. long end = System.currentTimeMillis();//结束时间
  36. System.out.println("传统IO读取数据,指定缓冲区大小,总共耗时:"+(end - start)+"ms");
  37. } catch (IOException e) {
  38. e.printStackTrace();
  39. }
  40. }

3、内存映射读文件

  1. /**
  2. * NIO 内存映射读大文件
  3. * @author linbingwen
  4. * @since  2015年9月15日
  5. * @param path
  6. */
  7. public static void readFile3(String path) {
  8. long start = System.currentTimeMillis();//开始时间
  9. long fileLength = 0;
  10. final int BUFFER_SIZE = 0x300000;// 3M的缓冲
  11. File file = new File(path);
  12. fileLength = file.length();
  13. try {
  14. MappedByteBuffer inputBuffer = new RandomAccessFile(file, "r").getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileLength);// 读取大文件
  15. byte[] dst = new byte[BUFFER_SIZE];// 每次读出3M的内容
  16. for (int offset = 0; offset < fileLength; offset += BUFFER_SIZE) {
  17. if (fileLength - offset >= BUFFER_SIZE) {
  18. for (int i = 0; i < BUFFER_SIZE; i++)
  19. dst[i] = inputBuffer.get(offset + i);
  20. } else {
  21. for (int i = 0; i < fileLength - offset; i++)
  22. dst[i] = inputBuffer.get(offset + i);
  23. }
  24. // 将得到的3M内容给Scanner,这里的XXX是指Scanner解析的分隔符
  25. Scanner scan = new Scanner(new ByteArrayInputStream(dst)).useDelimiter(" ");
  26. while (scan.hasNext()) {
  27. // 这里为对读取文本解析的方法
  28. System.out.print(scan.next() + " ");
  29. }
  30. scan.close();
  31. }
  32. System.out.println();
  33. long end = System.currentTimeMillis();//结束时间
  34. System.out.println("NIO 内存映射读大文件,总共耗时:"+(end - start)+"ms");
  35. } catch (Exception e) {
  36. e.printStackTrace();
  37. }
  38. }

三、测试对比

1、100M文件

文件大小如下:

调用如下:

  1. public static void main(String args[]) {
  2. String path = "D:" + File.separator + "CES_T_MSM_LIQ-TRANS-ESP_20150702_01.DAT";
  3. readFile1(path);
  4. //readFile2(path);
  5. //readFile3(path);
  6. }

(1)传统IO读取数据,不指定缓冲区大小,总共耗时:80264ms

其内存使用如下:

(2)传统IO读取数据,指定缓冲区大小,总共耗时:80612ms

其内存使用如下:

(3)NIO 内存映射读大文件,总共耗时:90955ms

其内存使用如下:

分析发现内存映射并没有比传统IO快多少,甚至还更加慢了,有可能是因为磁盘IO操作多了,反而降低了其效率,内存映射看来还是对大文件比较有好的效果。小文件基本上是没有多大的差别的。

2、1.2G文件

传统IO读取数据,不指定缓冲区大小,总共耗时:1245111ms

NIO 内存映射读大文件,总共耗时:1223877ms(大概20分钟多点)

 
 http://blog.csdn.net/evankaka/article/details/48464013

Java NIO内存映射---上G大文件处理(转)的更多相关文章

  1. Java内存映射,上G大文件轻松处理

    内存映射文件(Memory-mapped File),指的是将一段虚拟内存逐字节映射于一个文件,使得应用程序处理文件如同访问主内存(但在真正使用到这些数据前却不会消耗物理内存,也不会有读写磁盘的操作) ...

  2. Java NIO 内存映射文件

    Java NIO 内存映射文件 @author ixenos 文件操作的四大方法 前提:内存的访问速度比磁盘高几个数量级,但是基本的IO操作是直接调用native方法获得驱动和磁盘交互的,IO速度限制 ...

  3. JAVA NIO 内存映射(转载)

    原文地址:http://blog.csdn.net/fcbayernmunchen/article/details/8635427     Java类库中的NIO包相对于IO 包来说有一个新功能是内存 ...

  4. 【Web应用】JAVA网络上传大文件报500错误

    问题描述 当通过 JAVA 网站上传大文件,会报 500 错误. 问题分析 因为 Azure 的 Java 网站都是基于 IIS 转发的,所以我们需要关注 IIS 的文件上传限制以及 requestT ...

  5. java上传大文件解决方案

    需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在10G内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以10G来进行限制. 第一步: 前端修改 由于项目使用的是BJ ...

  6. Atitit.病毒木马的快速扩散机制原理nio 内存映射MappedByteBuffer

    Atitit.病毒木马的快速扩散机制原理nio 内存映射MappedByteBuffer 1. Java NIO(New Input/Output)1 1.1. 变更通知(因为每个事件都需要一个监听者 ...

  7. NIO内存映射

    磁盘的IO因为速度较慢,可能成为系统运行的瓶颈.所以磁盘的IO在操作系统级实现了提前读,延迟写的机制来提升IO的性能. 提前读就是一次读取需求的数据的同时多读接下来的一段数据至OS缓冲区中,延迟写就是 ...

  8. NIO入门之轻松读取大文件

    NIO入门之轻松读取大文件 今天同事碰到了一个问题,从游戏服务器下载下来的输出log有一个多G大.用记事本打不开,EditPlus也打不开,都提示文件太大.用word也打不开,提示文件大于512M.打 ...

  9. 【转】Python之mmap内存映射模块(大文本处理)说明

    [转]Python之mmap内存映射模块(大文本处理)说明 背景: 通常在UNIX下面处理文本文件的方法是sed.awk等shell命令,对于处理大文件受CPU,IO等因素影响,对服务器也有一定的压力 ...

随机推荐

  1. mvc action 有多种跳转

    在ASP.NET mvc下,action 有多种跳转方式: return RedirectToAction("Index");//一个参数时在本Controller下 如果Redi ...

  2. 以JTextPanel为例Swing的鼠标事件详解

    如下界面可以通过该界面研究一下Swing的鼠标事件: 图中用红粗线圈起来的为JtextPanel,该Panel添加了鼠标事件监听器,鼠标事件监听器有三种,分别为MouseWheelListener,M ...

  3. C++ 指针—02 指针与引用的对照

    ★同样点: ●都是地址的概念: 指针指向一块内存,它的内容是所指内存的地址:而引用则是某块内存的别名. ★不同点: ●指针是一个实体,而引用仅是个别名: ●引用仅仅能在定义时被初始化一次,之后不可变: ...

  4. ZooKeeper的安装、配置、启动和使用(一)——单机模式

    ZooKeeper的安装.配置.启动和使用(一)——单机模式 ZooKeeper的安装非常简单,它的工作模式分为单机模式.集群模式和伪集群模式,本博客旨在总结ZooKeeper单机模式下如何安装.配置 ...

  5. make 2>&1 | tee log.txt之小析

    前言 接触过linux的人,或多或少都会了解一点make 2>&1 | tee log.txt这个命令. 1. make是什么? make是linux下一个非常强大的命令,简单点就是你要 ...

  6. aix 下 实现goldengate 随os启动而自己主动启动的脚本

    aix 下 实现goldengate 随os启动而自己主动启动的脚本: 1.用oracle用户建立/u01/info.txt,文件内容例如以下: sh date start mgr 2.chmod + ...

  7. ARM架构和编程-4

    ARM中断异常处理: ARM系统中止品种:按中断处理降序排列优先级:重置.数据访问中止.高速中断请求.外部中断请求.预取中止.令.软件中断. ARM体系中的异常中断向量表: 0x0 复位 0x4 没有 ...

  8. HDU 3277 Marriage Match III(二分+最大流)

    HDU 3277 Marriage Match III 题目链接 题意:n个女孩n个男孩,每一个女孩能够和一些男孩配对,此外还能够和k个随意的男孩配对.然后有些女孩是朋友,满足这个朋友圈里面的人.假设 ...

  9. hdu2713(dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2713 题意:有N个点,每个点都有一个值x,每次进行跳跃,当跳到自己所跳的第奇数个点是+x,第偶数个点时 ...

  10. C#操作Cookie

    /* 创建者:菜刀居士的博客  * 创建日期: 2014年09月02号  * 功能:操作Cookie  *  */ namespace Net.String.ConsoleApplication { ...