最近在用java中的ByteBuffer,一直不明所以,尤其是对MappedByteBuffer使用的内存映射这个概念云里雾里。

于是首先补了物理内存、虚拟内存、页面内存、交换区的知识:小科普——物理内存、页面文件、交换区和虚拟内存

然后阅读了ByteBuffer的文章:ByteBuffer使用和实现以及文件内存映射。

ByteBuffer分为两种:间接地和直接的,所谓直接就是指MappedByteBuffer,直接使用内存映射(java的话就意味着在JVM之外分配虚拟地址空间);而间接的ByteBuffer是在JVM的堆上面的,说白了就是管理一群byte数组的包装。

这里面最核心最关键的也就是内存映射了,下面的内容来自这篇文件:内存映射文件原理探索

原理

首先,“映射”这个词,就和数学课上说的“一一映射”是一个意思,就是建立一种一一对应关系,在这里主要是指硬盘上文件的位置与进程逻辑地址空间中一块大小相同的区域之间的一一对应,如图1中过程1所示。这种对应关系纯属是逻辑上的概念,物理上是不存在的,原因是进程的逻辑地址空间本身就是不存在的。在内存映射的过程中,并没有实际的数据拷贝,文件没有被载入内存,只是逻辑上被放入了内存,具体到代码,就是建立并初始化了相关的数据结构(struct address_space),这个过程有系统调用mmap()实现,所以建立内存映射的效率很高。

图1.内存映射原理

既然建立内存映射没有进行实际的数据拷贝,那么进程又怎么能最终直接通过内存操作访问到硬盘上的文件呢?那就要看内存映射之后的几个相关的过程了。

mmap()会返回一个指针ptr,它指向进程逻辑地址空间中的一个地址,这样以后进程无需再调用read或write对文件进行读写,而只需要通过ptr就能够操作文件。但是ptr所指向的是一个逻辑地址,要操作其中的数据,必须通过MMU将逻辑地址转换成物理地址,如图1中过程2所示。这个过程与内存映射无关。

前面讲过,建立内存映射并没有实际拷贝数据,这时,MMU在地址映射表中是无法找到与ptr相对应的物理地址的,也就是MMU失败,将产生一个缺页中断,缺页中断的中断响应函数会在swap中寻找相对应的页面,如果找不到(也就是该文件从来没有被读入内存的情况),则会通过mmap()建立的映射关系,从硬盘上将文件读取到物理内存中,如图1中过程3所示。这个过程与内存映射无关。

如果在拷贝数据时,发现物理内存不够用,则会通过虚拟内存机制(swap)将暂时不用的物理页面交换到硬盘上,如图1中过程4所示。这个过程也与内存映射无关。

效率

从代码层面上看,从硬盘上将文件读入内存,都要经过文件系统进行数据拷贝,并且数据拷贝操作是由文件系统和硬件驱动实现的,理论上来说,拷贝数据的效率是一样的。但是通过内存映射的访问访问硬盘上的文件,效率要比read和write系统调用高,这是为什么呢?原因是read()是系统调用,其中进行了数据拷贝到用户空间,如图2中过程2,在这个过程中,实际上完成了两次数据拷贝;而mmap()也是系统调用,如前所述,mmap()中没有进行数据拷贝,真正的数据拷贝是在缺页中断处理时进行的,由于mmap()将文件直接映射到用户空间,所以中断处理函数根据这个映射关系,直接将文件从硬盘拷贝到用户空间,只进行了一次数据拷贝。因此,内存映射的效率要比read/write效率高。

图2.read系统调用原理

下面这个程序,通过read和mmap两种方法分别对硬盘上一个名为"mmap_test"的文件进行操作,文件中存有10000个证书,程序两次使用不同的方法将它们读出,加1,再写回硬盘。通过对比可以看出,read消耗的时间将近是mmap的两到三倍。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/mman.h>#define MAX 10000int main() {int i=0;int count=0, fd=0;struct timeval tv1, tv2;int *array = (int *)malloc(sizeof(int)*MAX);/*read*/gettimeofday(&tv1, NULL);fd = open("mman_test", O_RDWR);if (sizeof(int)*MAX != read(fd, (void *)array, sizeof(int)*MAX)) {printf("Reading data filed.../n");return -1;}for (i=0; i<MAX; ++i) {++array[i];}if (sizeof(int)*MAX != write(fd, (void *)array, sizeof(int)*MAX)) {printf("Writing data failed.../n");return -1;}free(array);close(fd);gettimeofday(&tv2, NULL);printf("Time of read/write: %dms\n", tv2.tv_usec-tv1.tv_usec);/*mmap*/gettimeofday(&tv1, NULL);fd = open("mmap_test", O_RDWR);array = mmap(NULL, sizeof(int)*MAX, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);for (i=0; i<MAX; ++i) {++array[i];}munmap(array, sizeof(int)*MAX);msync(array, sizeof(int)*MAX, MS_SYNC);free(array);close(fd);gettimeofday(&tv2, NULL);printf("Time of mmap: %dms\n", tv2.tv_usec - tv1.tv_usec);return 0;
}

输出结果:

Time of read/write: 154ms

Time of mmap: 68ms

MappedByteBuffer以及ByteBufer的底层原理相关推荐

  1. 没有与参数列表匹配的 重载函数 getline 实例_面试题:方法重载的底层原理?...

    前语:微信改版后,大量读者还没养成点赞的习惯,如写得好,望大家阅读后在右下边"好看"处点个赞,以示鼓励!长期坚持原创真的很不容易,多次想放弃,坚持是一种信仰,专注是一种态度. 关于 ...

  2. synchronized底层原理_你用过synchronized吗?它的底层原理是什么?Java经典面试题来了...

    并发编程已经成为程序员必备技能 作为Java程序员,不懂得并发编程显然已经不能满足市场需求了,尤其是在面试过程中将处于被动地位,也有可能面试将就此终结. 那么作为Java开发者的你,日常虽然可以基于J ...

  3. iOS底层原理 - 常驻线程

    iOS底层原理 - 常驻线程 在 AFN 2.0 时代,会经常看到 AFN 创建一个常驻线程的方式: 0️⃣ AFN 2.0 时代的常驻线程 + (NSThread *)networkRequestT ...

  4. elasticsearch原理_ElasticSearch读写底层原理及性能调优

    ES写入/查询底层原理 1. Elasticsearch写入数据流程 客户端随机选择一个ES集群中的节点,发送POST/PUT请求,被选择的节点为协调节点(coordinating node) 协调节 ...

  5. 嘿嘿,我就知道面试官接下来要问我 ConcurrentHashMap 底层原理了,看我怎么秀他...

    来自:烟雨星空 前言 上篇文章介绍了 HashMap 源码后,在博客平台广受好评,让本来己经不打算更新这个系列的我,仿佛被打了一顿鸡血.真的,被读者认可的感觉,就是这么奇妙. 原文:面试官再问你 Ha ...

  6. 面试官再问你 HashMap 底层原理,就把这篇文章甩给他看

    来自:烟雨星空 前言 HashMap 源码和底层原理在现在面试中是必问的.因此,我们非常有必要搞清楚它的底层实现和思想,才能在面试中对答如流,跟面试官大战三百回合.文章较长,介绍了很多原理性的问题,希 ...

  7. 为了把mysql的索引底层原理讲清楚,我把计算机翻了个底朝天

    来自:非科班的科班 什么是索引 概念:索引是提高mysql查询效率的数据结构.总的一句话概括就是索引是一种数据结构. 数据库查询是数据库的最主要功能之一.设计者们都希望查询数据的速度能尽可能的快,因此 ...

  8. 面试官:说说Spring Cloud底层原理?

    点击上方"蓝字", 右上角选择"设为星标" 周一至周五上午11:45!精品文章准时送上! 本文转载自公众号:石杉的架构笔记 目录 一.业务场景介绍 二.Spri ...

  9. Go语言底层原理剖析

    作者:郑建勋 出版社:电子工业出版社 品牌:博文视点 出版时间:2021-08-01 Go语言底层原理剖析

最新文章

  1. java基础提升篇:Java中Native关键字的作用
  2. linux 压缩解压归档
  3. Angular自学笔记(二)显示数据 绑定属性
  4. java ssm 系统的搭建_SSM框架下的javaweb学生管理系统--搭建系统结构
  5. 保留小数点后三位_【Meta分析】Stata制作森林图时,如何保留三位小数?
  6. 【英语学习】【English L06】U05 Appointments L2 I'd like to make an airport shuttle service reservation
  7. Redis连接的客户端(connected_clients)数过高或者不减的问题解决方案
  8. [工具开发] 移动梦网短信发送客户端
  9. (NeurIPS 2019) Gated CRF Loss -一种用于弱监督图像语义分割的新型损失函数
  10. [转载]手机编程与AIDE
  11. DXF格式文件导入AD,做成可加工的PCB文件
  12. python基础语法记录
  13. 一文带你详尽剖析Miracast投屏开发和调试
  14. 大一计算机基础教程期末考试,大一大学计算机基础教程期末考试题.doc
  15. windows cmd 提示 ‘系统找不到指定路径‘ 提示 ‘ECHO 处于关闭状态‘
  16. 虚拟仪器是在计算机基础上通过增加相关硬件和软件构建而成的仪器,无损检测考试...
  17. java中堆栈溢出_Java堆栈溢出
  18. 电脑新建文件夹快捷键
  19. 10、(十)外汇交易中专有名词整理
  20. PS3安装Linux Fedora Core 6教程

热门文章

  1. html section 布局,HTML:section标签
  2. c#分页_使用Kotlin搭配Springboot开发RESTFul接口(二)自定义配置、跨域、分页
  3. ES更新嵌套数组(使用Java API)
  4. linux 用 grep 查找单个或多个字符串(关键字)
  5. restTemplate踩过的坑-spring clound--cloud内部服务调用重试次数
  6. ParseObject 的使用简介【简单易懂】
  7. 【两种解法】1004 Counting Leaves (30 分)_27行代码AC
  8. 22行代码AC——L1-023 输出GPLT(~解题报告~)
  9. 单片机原理及其应用——单片机外部中断实验(八段数码管通过按键依次显示0~9数字)
  10. 普通平键的主要尺寸有_艾创米:解密指纹锁锁体尺寸测量方法