之前跟大家说过,要讲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()如果缓冲区的内容在物理内存中,则返回真,否则返回假

下面代码终于出场了

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

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

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption; public class TestMappedByteBuffer {
private static int length = 0x2FFFFFFF;//1G
private abstract static class Tester {
private String name;
public Tester(String name) {
this.name = name;
}
public void runTest() {
System.out.print(name + ": ");
long start = System.currentTimeMillis();
test();
System.out.println(System.currentTimeMillis()-start+" ms");
}
public abstract void test();
}
private static Tester[] testers = {
new Tester("Stream RW") {
public void test() {
try (FileInputStream fis = new FileInputStream(
"src/a.txt");
DataInputStream dis = new DataInputStream(fis);
FileOutputStream fos = new FileOutputStream(
"src/a.txt");
DataOutputStream dos = new DataOutputStream(fos);) { byte b = (byte)0;
for(int i=0;i<length;i++) {
dos.writeByte(b);
dos.flush();
}
while (dis.read()!= -1) {
}
} catch (IOException e) {
e.printStackTrace();
}
}
},
new Tester("Mapped RW") {
public void test() {
try (FileChannel channel = FileChannel.open(Paths.get("src/b.txt"),
StandardOpenOption.READ, StandardOpenOption.WRITE);) {
MappedByteBuffer mapBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, length);
for(int i=0;i<length;i++) {
mapBuffer.put((byte)0);
}
mapBuffer.flip();
while(mapBuffer.hasRemaining()) {
mapBuffer.get();
}
} catch (IOException e) {
e.printStackTrace();
}
}
},
new Tester("Mapped PRIVATE") {
public void test() {
try (FileChannel channel = FileChannel.open(Paths.get("src/c.txt"),
StandardOpenOption.READ, StandardOpenOption.WRITE);) {
MappedByteBuffer mapBuffer = channel.map(FileChannel.MapMode.PRIVATE, 0, length);
for(int i=0;i<length;i++) {
mapBuffer.put((byte)0);
}
mapBuffer.flip();
while(mapBuffer.hasRemaining()) {
mapBuffer.get();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
public static void main(String[] args) {
for(Tester tester:testers) {
tester.runTest();
}
}
}

先从整体上提一句上面的代码,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. window.location 小结)

    其实在网上都能找到,我只是总结一下,方便自己查找使用 示例URL:http://b.a.com:88/index.php?name=kang&when=2011#first 属性     含义 ...

  2. pods的安装和使用

    ////  pods的安装.h//  IOS笔记 /*Cocoapods安装步骤 1.升级Ruby环境 终端输入:$gem update --system 此时会出现 ERROR:  While ex ...

  3. AsyncTask 轻量级的异步类

    初步:http://www.cnblogs.com/devinzhang/archive/2012/02/13/2350070.html 详细:http://blog.csdn.net/liuhe68 ...

  4. 【转】vc++MCF/C++/C中怎样让应用程序获得或以管理员权限运行 ,ShellExecuteEX编程 --- 获取管理员权限

    ShellExecuteEX编程 --- 获取管理员权限:http://blog.csdn.net/jhui163/article/details/5873027 怎样让你的应用程序获得管理员权限:就 ...

  5. JS解析XML文件和XML字符串

    JS解析XML文件 <script type='text/javascript'> loadXML = function(xmlFile){ var xmlDoc=null; //判断浏览 ...

  6. 二、Spring——AoP

    AOP概述 AOP是OOP的有益补充,他为程序开发提供了一个崭新的思考角度,可以将重复性的横切逻辑抽取到统一的模块中,通过OOP的纵向抽象和AOP的横向抽取,程序才能真正解决复杂性代码问题. Spri ...

  7. python学习-day16:函数作用域、匿名函数、函数式编程、map、filter、reduce函数、内置函数r

    一.作用域 作用域在定义函数时就已经固定住了,不会随着调用位置的改变而改变 二.匿名函数 lambda:正常和其他函数进行配合使用.正常无需把匿名函数赋值给一个变量. f=lambda x:x*x p ...

  8. 对char类型的理解以及对补码的理解分析

    今天遇到这样一个小程序,觉得当中有些问题很容易让人忽略的! 这个程序代码如下: 程序的结果为: 我想很多像我一样的小白可能才开始是想不明白为什么最后的结果是255吧!首先,我们得知道 strlen() ...

  9. sql 数据量高并发的数据库优化(转)

    Mysql 大数据量高并发的数据库优化 一.数据库结构的设计 如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能.所以,在一个系统开始实 ...

  10. 2 、Linux基本命令-ls-pwd-cd-date-hwclock

    Linux基本命令: 1.ls-查看目录下的文档 语法: ls 目录 注: .当前目录  ..上级目录 如:ls /etc/ 相关参数: -l  显示详细信息 ls /etc/ -l -a 显示隐藏的 ...