转载自  学习笔记之ByteBuffer使用和实现以及文件内存映射

ByteBuffer和CharBuffer等其它Buffer的直接子类一样,顾名思义,就是存取字节的Buffer。很多数据最终在和底层交互上都是使用了字节,而更大的数据是由字节组合而成。谈到字节的组合,就不得不谈到字节大小的定义和字节的顺序。关于字节是8位构成的这个结论,似乎现在的计算机教材都理所当然地描述出来,我们也默认接受了这样的一个事实。但实际上字节由8个二进制位构成也是有渊源和优点的,这与IBM的360主机有关,详细的可以参考这个。下面说说组成数据的字节顺序。

对于多字节的数据在系统中的存储,通常按数据的高位和低位在系统内存中的高地址和低地址存放分为大端(big endian)和小端(little endian)两种方式。

在Java API中,有ByteOrder这样一个public类,在其中定义了大端和小端两个常量。通过这个java.nio.ByteOrder类的nativeOrder()方法,也可以确定当前系统平台的字节顺序。

在不同的平台上可能有不同的字节顺序标准。但在ByteBuffer类中,默认是使用了ByteOrder.BIG_ENDIAN字节序。但可以通过ByteBuffer的重载方法获取和设置字节序:

  • public final ByteOrder order( )
  • public final ByteBuffer order (ByteOrder bo)

1. ByteBuffer的实现

提到ByteBuffer的实现,我们先来看下Win下JDK实现的类层次结构图。

Win下JDK的ByteBuffer类层次结构图

而在Oracle的Java SE API中,实际上只提到了MappedByteBuffer。所以堆实现和具体的直接实现(DirectByteBuffer)我们只简单了解就行了,因为这个不在API中,和平台实现相关。

HeapByteBuffer 是虚拟机的堆中实现,DirectByteBuffer是系统级别实现(使用unsafe的 unsafe.allocateMemory(size)),使用时后者比前者节省了拷贝过程,但后者的构建和析构成本更高,总体性能需要具体问题综合分析。而且DirectByteBuffer会受到平台方面的约束,使用时需要小心注意。

而API中出现了的java.nio.MappedByteBuffer则是针对文件映射工作的,也是一种Direct的ByteBuffer。除了继承ByteBuffer类的方法外,API还提供了下面3个方法:

  • public final MappedByteBuffer force()
  • public final boolean isLoaded()
  • public final MappedByteBuffer load()

关于文件映射相关的具体内容,下面会详细说。

2. ByteBuffer和Buffer的其它直接子类之间的关系

前面的一篇Buffer的文章提到了它的几个直接子类,分别是ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer。除了ByteBuffer,还有另外6种,而这些也都和Java的基本数据类型有一定的对应关系,下面我们对使用上的情况梳理下。

ByteBuffer继承于Buffer。和CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer一样,都是抽象类。

在Java NIO中,除了Buffer还有Channel等类。Buffer也只有和Channel配合才能充分地把Java NIO使用起来。而Channel中的很多方法都是使用ByteBuffer作为参数和返回结果进行传递的,好处就是Byte是字节、是基础,而方法也简单了很多。

而这样的设计有一个要求,就是使用Byte以外的类型也能很好的利用到ByteBuffer,这其中有两种方式:

  • 一种是View Buffer,即直接通过ByteBuffer的数据结构做支持,得到另外一种类型Buffer对象
  • 另一种就是Data Element View,即不通过CharBuffer等类对象和ByteBuffer的互相转换获取,而是直接使用ByteBuffer自带的基本类型put和get方法

下面是两段代码例子。

ByteBuffer byteBuffer = ByteBuffer.allocate (7).order (ByteOrder.BIG_ENDIAN);
CharBuffer charBuffer = byteBuffer.asCharBuffer( );intvalue = buffer.getInt( );

当然,在实际使用过程中也会有需要注意的问题,比如字符数据,就需要考虑字符集编码的问题。下面是《Thinking in Java》中的例子:

publicclass  BufferToText {privatestatic  final  int  BSIZE = 1024;publicstatic  void  main(String[] args)throwsException {FileChannel fc = newFileOutputStream("data2.txt").getChannel();fc.write(ByteBuffer.wrap("Some text".getBytes()));fc.close();fc = newFileInputStream("data2.txt").getChannel();ByteBuffer buff = ByteBuffer.allocate(BSIZE);fc.read(buff);buff.flip();// Doesn’t work:System.out.println(buff.asCharBuffer());// Decode using this system’s default Charset:buff.rewind();String encoding = System.getProperty("file.encoding");System.out.println("Decoded using "+ encoding + ": "+ Charset.forName(encoding).decode(buff));// Or, we could encode with something that will print:fc = newFileOutputStream("data2.txt").getChannel();fc.write(ByteBuffer.wrap("Some text".getBytes("UTF-16BE")));fc.close();// Now try reading again:fc = newFileInputStream("data2.txt").getChannel();buff.clear();fc.read(buff);buff.flip();System.out.println(buff.asCharBuffer());// Use a CharBuffer to write through:fc = newFileOutputStream("data2.txt").getChannel();buff = ByteBuffer.allocate(24); // More than neededbuff.asCharBuffer().put("Some text");fc.write(buff);fc.close();// Read and display:fc = newFileInputStream("data2.txt").getChannel();buff.clear();fc.read(buff);buff.flip();System.out.println(buff.asCharBuffer());}
}

3. 内存映射和ByteBuffer的使用

这段内容将简单说明下文件内存映射的概念和ByteBuffer的其它点。

ByteBuffer的最基本使用,和上一篇NIO中讲CharBuffer等Buffer的直接子类一样,就是put()和get()。为提高效率,除了单个字节读写,有整块的操作方法,即对get()和put()的重载方法。

而内存映射这个概念最初一直困惑了我很久才搞明白,但实际上原理并不复杂,只需要了解操作系统工作的最基本原理。我们通常的直接通过API做IO,会用到一系列的系统调用(system call),之后通过驱动程序和外部设备交互来完成输入输出操作,磁盘上的文件读写也是一样。而文件内存映射之所以得到很好的使用是因为,使用了文件的内存映射可以大大提高效率。提高效率的点就在于,不必每个IO操作都经过系统调用来完成,这个效率是相对较低的,而是巧妙灵活地使用内存管理系统。我们都知道当程序需要使用大量内存而实际物理内存较小的时候,我们的内存管理系统会进行页的换入换出操作,使部分当前使用不到的内存页放到磁盘上去,而缺页中断又会相应的做换入操作 —— 这就是内存映射的基础。

在Java中,在Java NIO的FileChannel类中,提供了一个map()方法,这个方法返回的结果就是一个MappedByteBuffer类的对象,也就是一个ByteBuffer对象。这使得我们对磁盘上文件内容的读写,完全可以像对其他Buffer一样,进行put()和get()。

4. 其它一些实现细节

这是一些未深入整理的实现细节点,在Win下的Oracle/Sun JDK:

  • ByteBuffer的具体实现,也是基于byte数组和对应的offset
  • 有array()和arrayOffset()抽象未实现方法
  • 还有address属性,DirectBuffer才会用到
  • 还有只读等属性和其它方法等

学习笔记之ByteBuffer使用和实现以及文件内存映射相关推荐

  1. oracle 11g dul,学习笔记:Oracle dul数据挖掘 导出Oracle11G数据文件坏块中表中

    试验模拟导出Oracle 11G数据库中数据文件坏块中表中的数据 以前一直以为dul对应的版本只能恢复最高的数据库版本一致,今天测试发现dul 10可以恢复11g最新版的数据库. 模拟环境SQL> ...

  2. OpenGL超级宝典学习笔记:着色器存储区块、原子内存操作、内存屏障

    前言 本篇在讲什么 本篇为蓝宝书学习笔记 着色器存储区块 原子内存操作 内存屏障 本篇适合什么 适合初学Open的小白 本篇需要什么 对 C++语法有简单认知 对 OpenGL有简单认知 最好是有 O ...

  3. Java学习笔记(二)——Java操作properties文件

    [前面的话] 前段时间在学习和玩java web相关的东西,对于这些技术,一边学习,一边做东西,一边总结,希望可以一边成长和有所收获.有时总是思考太多反而成为了前进的阻力,所以对于生活还是简单一些,不 ...

  4. 小滴课堂-项目大课学习笔记(2)海量数据存储-分布式文件存储系统

    在了解分布式文件存储之前,我们可以先来了解一下什么是分布式存储,分布式存储的系统又分为哪些 什么是分布式存储 在近些年来,随着各大的互联网公司的大数据应用的崛起,分布式系统被广泛的投入到实践当中.互联 ...

  5. webpack学习笔记(三):监听文件变化并编译

    在上一篇webpack学习笔记中主要认识了webpack配置文件中相关的基础配置和命令的执行.这次学习如何在文件发生变化时自动打包编译. 首先,我们来看一下配置文件 const path = requ ...

  6. Git学习笔记一--创建版本库、添加文件、提交文件等

    Git,是Linus花了两周时间用C写的一个分布式版本控制系统.牛该怎么定义? 其实,很多人都不care谁写了Git,只在乎它是免费而且好用的!So do I! 下面开始我们的学习: 1.Git安装( ...

  7. Xilinx 学习笔记1---新建工程和创建源代码文件

    最近终于有空可以记录一些之前学习的内容,本博客系列记录笔者Xilinx ISE学习之旅,当然现在Vivado是学习热门,一步一步来.搞定 ISE,Vivado上手也会很快. 1.安装软件 软件部分的下 ...

  8. vim学习笔记一(环境配置及编译文件)

    今天开始在linux进行编程学习,首先是环境的配置.使用的ubuntu系统自带的vim.但是在使用前要先对vim进行配置,使其更友好. 配置方法是对vimrc进行修改,分享下我的vimrc,(下载地址 ...

  9. Kali学习笔记31:目录遍历漏洞、文件包含漏洞

    文章的格式也许不是很好看,也没有什么合理的顺序 完全是想到什么写一些什么,但各个方面都涵盖到了 能耐下心看的朋友欢迎一起学习,大牛和杠精们请绕道 目录遍历漏洞: 应用程序如果有操作文件的功能,限制不严 ...

最新文章

  1. Spring(07)——单例注入多例之lookup-method
  2. 域名端口自己电脑做服务器续,用默认端口,自动跳转到默认项目 【直接用域名访问我们本机的项目】...
  3. Android LayoutInflater.inflate源码解析
  4. mybatis中mysql流式读取_MyBatis读取大量数据(流式读取)
  5. 第四范式团队KDD Cup世界冠军方案详解:解密共享出行场景中的优化问题
  6. ABP vNext IOC替换原有Service实现
  7. php 编写线程教程,php 实现多线程
  8. java 使用jar_Java 使用JAR文件
  9. 视频营销:影响视频排名的五个重要因素
  10. 中文生成罗马音_现代建筑寿命仅50年,古罗马混凝土为何2000年越来越坚固?
  11. express 设置header解决跨域问题
  12. GROUP BY中ROLLUP/CUBE/GROUPING/GROUPING SETS使用示例
  13. java中native方法实现_详解Java中native方法的使用
  14. 找101-200之间的素数(Java实现)
  15. 借助网盘离线下载从官网下载DB2-Express C
  16. 微信小程序之获取百度语音合成
  17. 不用计算机怎么连接无线,不用电脑?手机如何调试无线路由器?
  18. 极验验证--滑块验证
  19. 超声波传感器与液晶屏显示实验
  20. OSS云文件列举分页功能的解决方法

热门文章

  1. 关于前端性能优化问题,认识网页加载过程和防抖节流
  2. 非递归遍历二叉树(算法导论第三版第十章10.4-5)
  3. 字典树模板+洛谷P2580 于是他错误的点名开始了
  4. java基础输入输出语句
  5. 数据结构与算法--第一个只出现一次的字符
  6. diff git 指定时间,git diff日期?
  7. nvcc找不到的问题(Ubuntu16.04 CUDA 8.0)
  8. Codeforces Round #715 (Div. 1) C. Complete the MST 补图 + 思维 + 最小生成树
  9. Educational Codeforces Round 88 D. Yet Another Yet Another Task(巧枚举)
  10. CF1158D. Beautiful Array