之前跟大家说过,要讲MappedByteBuffer,现在我来履行承诺了。

首先从大体上讲一下MappedByteBuffer究竟是什么。从继承结构上来讲,MappedByteBuffer继承自ByteBuffer,所以ByteBuffer有的能力它全有;像变动position和limit指针啦、包装一个其他种类Buffer的视图啦,都可以。“MappedByteBuffer”为何而来?吾辈心中亦有惑(熊猫人之谜的梗)用一个字来概括就是

为什么?因为它使用direct buffer的方式读写文件内容,这种方式的学名叫做内存映射。这种方式直接调用系统底层的缓存,没有JVM和系统之间的复制操作,所以效率大大的提高了。而且由于它这么快,还可以用它来在进程(或线程)间传递消息,基本上能达到和“共享内存页”相同的作用,只不过它是依托实体文件来运行的。

而且它还有另一种能力。就是它可以让我们读写那些因为太大而不能放进内存中的文件。有了它,我们就可以假定整个文件都放在内存中(实际上,大文件放在内存和虚拟内存中),基本上都可以将它当作一个特别大的数组来访问,这样极大的简化了对于大文件的修改等操作。

下面我们开始介绍它的用法了

FileChannel提供了map方法来把文件映射为MappedByteBuffer: MappedByteBuffer map(int mode,long position,long size); 可以把文件的从position开始的size大小的区域映射为MappedByteBuffer,mode指出了可访问该内存映像文件的方式,共有三种,分别为:

MapMode.READ_ONLY(只读): 试图修改得到的缓冲区将导致抛出 ReadOnlyBufferException。

MapMode.READ_WRITE(读/写): 对得到的缓冲区的更改最终将写入文件;但该更改对映射到同一文件的其他程序不一定是可见的(无处不在的“一致性问题”又出现了)。

MapMode.PRIVATE(专用): 可读可写,但是修改的内容不会写入文件,只是buffer自身的改变,这种能力称之为”copy on write”

再简单的说一下,MappedByteBuffer较之ByteBuffer新增的三个方法

  • fore()缓冲区是READ_WRITE模式下,此方法对缓冲区内容的修改强行写入文件
  • load()将缓冲区的内容载入内存,并返回该缓冲区的引用
  • isLoaded()如果缓冲区的内容在物理内存中,则返回真,否则返回假

下面代码终于出场了

  1. int length = 0x8FFFFFF;//一个byte占1B,所以共向文件中存128M的数据
  2. try (FileChannel channel = FileChannel.open(Paths.get("src/c.txt"),
  3. StandardOpenOption.READ, StandardOpenOption.WRITE);) {
  4. MappedByteBuffer mapBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, length);
  5. for(int i=0;i<length;i++) {
  6. mapBuffer.put((byte)0);
  7. }
  8. for(int i = length/2;i<length/2+4;i++) {
  9. //像数组一样访问
  10. System.out.println(mapBuffer.get(i));
  11. }
  12. }

上面是MappedByteBuffer最基本的应用,而下面这段代码主要是测试它到底有多快,

  1. import java.io.DataInputStream;
  2. import java.io.DataOutputStream;
  3. import java.io.FileInputStream;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. import java.nio.MappedByteBuffer;
  7. import java.nio.channels.FileChannel;
  8. import java.nio.file.Paths;
  9. import java.nio.file.StandardOpenOption;
  10. public class TestMappedByteBuffer {
  11. private static int length = 0x2FFFFFFF;//1G
  12. private abstract static class Tester {
  13. private String name;
  14. public Tester(String name) {
  15. this.name = name;
  16. }
  17. public void runTest() {
  18. System.out.print(name + ": ");
  19. long start = System.currentTimeMillis();
  20. test();
  21. System.out.println(System.currentTimeMillis()-start+" ms");
  22. }
  23. public abstract void test();
  24. }
  25. private static Tester[] testers = {
  26. new Tester("Stream RW") {
  27. public void test() {
  28. try (FileInputStream fis = new FileInputStream(
  29. "src/a.txt");
  30. DataInputStream dis = new DataInputStream(fis);
  31. FileOutputStream fos = new FileOutputStream(
  32. "src/a.txt");
  33. DataOutputStream dos = new DataOutputStream(fos);) {
  34. byte b = (byte)0;
  35. for(int i=0;i<length;i++) {
  36. dos.writeByte(b);
  37. dos.flush();
  38. }
  39. while (dis.read()!= -1) {
  40. }
  41. } catch (IOException e) {
  42. e.printStackTrace();
  43. }
  44. }
  45. },
  46. new Tester("Mapped RW") {
  47. public void test() {
  48. try (FileChannel channel = FileChannel.open(Paths.get("src/b.txt"),
  49. StandardOpenOption.READ, StandardOpenOption.WRITE);) {
  50. MappedByteBuffer mapBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, length);
  51. for(int i=0;i<length;i++) {
  52. mapBuffer.put((byte)0);
  53. }
  54. mapBuffer.flip();
  55. while(mapBuffer.hasRemaining()) {
  56. mapBuffer.get();
  57. }
  58. } catch (IOException e) {
  59. e.printStackTrace();
  60. }
  61. }
  62. },
  63. new Tester("Mapped PRIVATE") {
  64. public void test() {
  65. try (FileChannel channel = FileChannel.open(Paths.get("src/c.txt"),
  66. StandardOpenOption.READ, StandardOpenOption.WRITE);) {
  67. MappedByteBuffer mapBuffer = channel.map(FileChannel.MapMode.PRIVATE, 0, length);
  68. for(int i=0;i<length;i++) {
  69. mapBuffer.put((byte)0);
  70. }
  71. mapBuffer.flip();
  72. while(mapBuffer.hasRemaining()) {
  73. mapBuffer.get();
  74. }
  75. } catch (IOException e) {
  76. e.printStackTrace();
  77. }
  78. }
  79. }
  80. };
  81. public static void main(String[] args) {
  82. for(Tester tester:testers) {
  83. tester.runTest();
  84. }
  85. }
  86. }

先从整体上提一句上面的代码,runTest()是一个模板方法,并且引用了一个未实现的test()方法;通过匿名内部类的实现,填充了测试内容。

再来说上面代码的测试结果。用传统流的方式,当然是最慢的,但应该是由于用的数据量是1G,无法全部读入内存,所以它根本无法完成测试。剩下两种MapMode.READ_WRITEMapMode.PRIVATE各有特点,首先说MapMode.READ_WRITE,它的速度每次差别较大,在0.6s和8s之间波动,而且很不稳定。但MapMode.PRIVATE就稳得出奇,一直是1.1s到1.2s之间。但无论是哪个速度都是十分惊人的。但是MappedByteBuffer也有不足,就是在数据量很小的时候,表现比较糟糕,那是因为direct buffer的初始化时间较长,所以建议大家只有在数据量较大的时候,在用MappedByteBuffer。

还要强调的一点是,MappedByteBuffer存在内存占用和文件关闭等不确定问题。被MappedByteBuffer打开的文件只有在垃圾收集时才会被关闭,而这个点是不确定的。javadoc里是这么说的:

A mapped byte buffer and the file mapping that it represents remain valid until the buffer itself is garbage-collected. ——JavaDoc

关于MappedByteBuffer就告诉你这么多了,有什么问题尽管提、有什么想法随时找我交流。

Java NIO教程 MappedByteBuffer的更多相关文章

  1. Java NIO教程 目录

    "Java NIO系列教程" 是笔者hans为NIO的初学者编写的一份入门教程,想仔细学习的同学可以按照顺序去阅读.由于我学的也不是特别的精,所以错误.疏漏在所难免,希望同学们指正 ...

  2. 海纳百川而来的一篇相当全面的Java NIO教程

    目录 零.NIO包 一.Java NIO Channel通道 Channel的实现(Channel Implementations) Channel的基础示例(Basic Channel Exampl ...

  3. Java NIO 教程

    Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API.本系列教程将有助于你学习和理解Java NIO. Java NIO提供了与 ...

  4. 【JavaNIO的深入研究4】内存映射文件I/O,大文件读写操作,Java nio之MappedByteBuffer,高效文件/内存映射

    内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件.有了内存映射文件,你就可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问.这种解决办法能大大简化修改文件的代码.fileC ...

  5. java nio 之MappedByteBuffer

    其实掌握MappedByteBuffer并不难,只要记住"三方三法三特性"(我自己总结的,呵呵~~不要扔鸡蛋哦...)这句话就可以轻松搞定!MappedByteBuffer 只是一 ...

  6. java大文件读写操作,java nio 之MappedByteBuffer,高效文件/内存映射

    java处理大文件,一般用BufferedReader,BufferedInputStream这类带缓冲的Io类,不过如果文件超大的话,更快的方式是采用MappedByteBuffer. Mapped ...

  7. Java NIO教程 前言

    阅读本文前,建议你先了解 旧I/O NIO 是 New I/O 的缩写,要了解它真正的内涵,需要掌握的知识还是比较多的.我努力在这几篇笔记里,勾勒出整个io的面貌.为大家的深入学习铺路. I/O简史 ...

  8. Java NIO教程 文件系统

    在NIO.2的文件系统中,Path是一切操作的基础.Path准确来说,代表着文件系统中的位置.可以代表一个目录(也就是通常所说的文件夹),也可以代表一个文件. 在新文件系统中,还有一个不得不说的就是F ...

  9. [翻译] java NIO 教程---介绍

    原文地址:http://tutorials.jenkov.com/java-nio/index.html Java NIO(new IO)是从java1.4之后的对IO API的另一种选择,即对标准j ...

随机推荐

  1. 《C与指针》第四章练习

    本章问题 1.Is the following statement legal?If so,what does it do? (下面的语句是否合法,如果合法,它做了什么) 3 * x * x - 4 ...

  2. [dijkstra+heap优化] 模板

    var n,m,s,i,j,x,y,z,l,tot :longint; pre,last,other,len :..] of longint; heap,d,pl :Array[..] of long ...

  3. archlinux 学习笔记

    磁盘规划 cfdisk 格式化分区 mkfs.ext4 /dev/sda1 mkswap /dev/sda5 mkfs.ext4 /dev/sda6 挂载根分区和boot分区,并建立家目录 mount ...

  4. UVa 679 小球下落

    题意:这道题规律性极强,虽然是二叉树,但是可以用模拟来写. 1<<20 意思是1的二进制左移20位,即2的20次方. 对于二叉树中一个节点 k ,其左节点,右节点的编号分别是2k 和 2k ...

  5. poj3592 强连通+记忆化搜索

    题意:有一片 n*m 的矿地,每一格有矿.或这传送门.或者挡路岩石.除了岩石不能走以外,其他的格子都能够向右或向下走,走到一个非岩石的格子.对于每一个矿点,经过它就能得到它的所有矿石,而对于每一个传送 ...

  6. HTML 参考手册

    按字母顺序排列 New : HTML5 中的新标签. 标签 描述 <!--...--> 定义注释. <!DOCTYPE>  定义文档类型. <a> 定义锚. < ...

  7. HackerRank "Angry Children 2"

    Fun one! A combination of Greedy and DP. The solution sparkled in my mind - I almost lost it.. Greed ...

  8. JavaScript中 Promise的学习以及使用

    今天一个哥们发过来一段js代码,没看懂,就顺便学习了一下,代码如下  Promise.resolve('zhangkai').then(value => {console.log(value)} ...

  9. Centos6.4 用rpm方式安装MySql5.6

    1.查看系统是否安装了MySQL     使用命令:     #rpm -qa | grep mysql    2.卸载已安装的MySQL      卸载mysql命令如下:       #rpm - ...

  10. Angular SEO方案

    1.如果是java web项目,可以直接使用AngularSEO Filter. 官网地址 :http://www.angularseo.net/#about <filter> <f ...