0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

JAVA中NIO通过MappedByteBuffer操作大文件

汽车玩家 来源:IT知识课堂 作者:IT知识课堂 2020-05-05 23:42 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

java io操作中通常采用BufferedReader,BufferedInputStream等带缓冲的IO类处理大文件,不过java nio中引入了一种基于MappedByteBuffer操作大文件的方式,其读写性能极高,本文会介绍其性能如此高的内部实现原理。

内存管理

在深入MappedByteBuffer之前,先看看计算机内存管理的几个术语:

MMC:CPU的内存管理单元。

物理内存:即内存条的内存空间。

虚拟内存:计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。

页面文件:操作系统反映构建并使用虚拟内存的硬盘空间大小而创建的文件,在windows下,即pagefile.sys文件,其存在意味着物理内存被占满后,将暂时不用的数据移动到硬盘上。

缺页中断:当程序试图访问已映射在虚拟地址空间中但未被加载至物理内存的一个分页时,由MMC发出的中断。如果操作系统判断此次访问是有效的,则尝试将相关的页从虚拟内存文件中载入物理内存。

为什么会有虚拟内存和物理内存的区别?
如果正在运行的一个进程,它所需的内存是有可能大于内存条容量之和的,如内存条是256M,程序却要创建一个2G的数据区,那么所有数据不可能都加载到内存(物理内存),必然有数据要放到其他介质中(比如硬盘),待进程需要访问那部分数据时,再调度进入物理内存。

什么是虚拟内存地址和物理内存地址?
假设你的计算机是32位,那么它的地址总线是32位的,也就是它可以寻址00xFFFFFFFF(4G)的地址空间,但如果你的计算机只有256M的物理内存0x0x0FFFFFFF(256M),同时你的进程产生了一个不在这256M地址空间中的地址,那么计算机该如何处理呢?回答这个问题前,先说明计算机的内存分页机制。

计算机会对虚拟内存地址空间(32位为4G)进行分页产生页(page),对物理内存地址空间(假设256M)进行分页产生页帧(page frame),页和页帧的大小一样,所以虚拟内存页的个数势必要大于物理内存页帧的个数。在计算机上有一个页表(page table),就是映射虚拟内存页到物理内存页的,更确切的说是页号到页帧号的映射,而且是一对一的映射。
问题来了,虚拟内存页的个数 > 物理内存页帧的个数,岂不是有些虚拟内存页的地址永远没有对应的物理内存地址空间?不是的,操作系统是这样处理的。操作系统有个页面失效(page fault)功能。操作系统找到一个最少使用的页帧,使之失效,并把它写入磁盘,随后把需要访问的页放到页帧中,并修改页表中的映射,保证了所有的页都会被调度。

现在来看看什么是虚拟内存地址和物理内存地址:

虚拟内存地址:由页号(与页表中的页号关联)和偏移量(页的小大,即这个页能存多少数据)组成。

举个例子,有一个虚拟地址它的页号是4,偏移量是20,那么他的寻址过程是这样的:首先到页表中找到页号4对应的页帧号(比如为8),如果页不在内存中,则用失效机制调入页,接着把页帧号和偏移量传给MMC组成一个物理上真正存在的地址,最后就是访问物理内存的数据了。

MappedByteBuffer是什么

从继承结构上看,MappedByteBuffer继承自ByteBuffer,内部维护了一个逻辑地址address。

示例

通过MappedByteBuffer读取文件

JAVA中NIO通过MappedByteBuffer操作大文件

map过程

FileChannel提供了map方法把文件映射到虚拟内存,通常情况可以映射整个文件,如果文件比较大,可以进行分段映射。

FileChannel中的几个变量:MapMode mode:内存映像文件访问的方式,共三种: MapMode.READ_ONLY:只读,试图修改得到的缓冲区将导致抛出异常。 MapMode.READ_WRITE:读/写,对得到的缓冲区的更改最终将写入文件;但该更改对映射到同一文件的其他程序不一定是可见的。 MapMode.PRIVATE:私用,可读可写,但是修改的内容不会写入文件,只是buffer自身的改变,这种能力称之为”copy on write”。position:文件映射时的起始位置。allocationGranularity:Memory allocation size for mapping buffers,通过native函数initIDs初始化。

接下去通过分析源码,了解一下map过程的内部实现。

通过RandomAccessFile获取FileChannel。

JAVA中NIO通过MappedByteBuffer操作大文件

上述实现可以看出,由于synchronized ,只有一个线程能够初始化FileChannel。

通过FileChannel.map方法,把文件映射到虚拟内存,并返回逻辑地址address,实现如下:

JAVA中NIO通过MappedByteBuffer操作大文件

上述代码可以看出,最终map通过native函数map0完成文件的映射工作。
1. 如果第一次文件映射导致OOM,则手动触发垃圾回收,休眠100ms后再次尝试映射,如果失败,则抛出异常。
2. 通过newMappedByteBuffer方法初始化MappedByteBuffer实例,不过其最终返回的是DirectByteBuffer的实例,实现如下:

JAVA中NIO通过MappedByteBuffer操作大文件

由于FileChannelImpl和DirectByteBuffer不在同一个包中,所以有权限访问问题,通过AccessController类获取DirectByteBuffer的构造器进行实例化。

DirectByteBuffer是MappedByteBuffer的一个子类,其实现了对内存的直接操作。

get过程

MappedByteBuffer的get方法最终通过DirectByteBuffer.get方法实现的。

JAVA中NIO通过MappedByteBuffer操作大文件

map0()函数返回一个地址address,这样就无需调用read或write方法对文件进行读写,通过address就能够操作文件。底层采用unsafe.getByte方法,通过(address + 偏移量)获取指定内存的数据。

第一次访问address所指向的内存区域,导致缺页中断,中断响应函数会在交换区中查找相对应的页面,如果找不到(也就是该文件从来没有被读入内存的情况),则从硬盘上将文件指定页读取到物理内存中(非jvm堆内存)。

如果在拷贝数据时,发现物理内存不够用,则会通过虚拟内存机制(swap)将暂时不用的物理页面交换到硬盘的虚拟内存中。

性能分析

从代码层面上看,从硬盘上将文件读入内存,都要经过文件系统进行数据拷贝,并且数据拷贝操作是由文件系统和硬件驱动实现的,理论上来说,拷贝数据的效率是一样的。
但是通过内存映射的方法访问硬盘上的文件,效率要比read和write系统调用高,这是为什么?

read()是系统调用,首先将文件从硬盘拷贝到内核空间的一个缓冲区,再将这些数据拷贝到用户空间,实际上进行了两次数据拷贝;

map()也是系统调用,但没有进行数据拷贝,当缺页中断发生时,直接将文件从硬盘拷贝到用户空间,只进行了一次数据拷贝。

所以,采用内存映射的读写效率要比传统的read/write性能高。

总结

MappedByteBuffer使用虚拟内存,因此分配(map)的内存大小不受JVM的-Xmx参数限制,但是也是有大小限制的。

如果当文件超出1.5G限制时,可以通过position参数重新map文件后面的内容。

MappedByteBuffer在处理大文件时的确性能很高,但也存在一些问题,如内存占用、文件关闭不确定,被其打开的文件只有在垃圾回收的才会被关闭,而且这个时间点是不确定的。
javadoc中也提到:A mapped byte buffer and the file mapping that it represents remain valid until the buffer itself is garbage-collected.*

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 内存
    +关注

    关注

    9

    文章

    3173

    浏览量

    76099
  • JAVA
    +关注

    关注

    20

    文章

    2997

    浏览量

    115628
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    飞凌嵌入式ElfBoard-文件I/O的了解探究之Linux系统的文件管理机制

    的inode编号。② 通过inode编号从inode table中找到对应的inode结构体。 ③ 根据inode结构体记录的信息,确定文件数据所在的block,并读出数据。文件打开
    发表于 11-18 08:50

    Air8000工业引擎WiFi AP文件管理功能操作指南

    、WiFi AP 文件管理系统概述   本文档介绍基于 Air8000 工业引擎实现的 WiFi AP 文件管理系统功能,通过该功能可以将设备变身为一个轻量级的文件服务器,用户只需连接
    的头像 发表于 10-29 15:23 115次阅读
    Air8000工业引擎WiFi AP<b class='flag-5'>文件</b>管理功能<b class='flag-5'>操作</b>指南

    飞凌嵌入式ElfBoard-系统应用编程之文件I/O

    ,在必要时再去通过系统调用读写实际文件,从而减少系统调用次数,避免系统资源的浪费。缓冲分有三类:全缓冲:操作的数据会存储到缓冲区,在缓冲区填满后再进行对实际
    发表于 10-24 08:57

    ESP32驱动SPIFFS进行文件操作

    本篇文章介绍如何在ESP32开发板上使用SPIFFS(SPI Flash File System)进行文件操作。看下如何初始化SPIFFS文件系统、读取文件、列出
    的头像 发表于 08-05 18:11 3894次阅读
    ESP32驱动SPIFFS进行<b class='flag-5'>文件</b><b class='flag-5'>操作</b>

    大文件高效传输不求人!Ymodem协议实战示例与核心技巧揭秘

    无需复杂网络环境,Ymodem协议即可实现可靠的大文件传输!通过其简洁的通信机制(如SOH帧头、数据分块、ACK/NACK反馈),无论是单片机通信还是跨平台传输,本文示例将演示如何快速部署,并
    的头像 发表于 07-28 17:38 730次阅读
    <b class='flag-5'>大文件</b>高效传输不求人!Ymodem协议实战示例与核心技巧揭秘

    深度操作系统deepin 25跨端协同应用升级

    在日常使用 deepin 办公与生活,跨设备协作的效率问题时常困扰着我们:多设备间传输文件依赖社交工具,速度慢且大文件受限;多台设备办公时频繁切换键鼠,打断思路且易出错;跨设备共享剪贴板内容更是繁琐,往往需要多次中转
    的头像 发表于 07-15 09:51 1146次阅读

    可以通过SPP协议传输大文件吗?

    我正在使用 CYBT-343026 (CYW-20706 Silicon) 模块。 我根据 SPP 样本制作了一个操作 SPP 的应用程序。 但是,传输大数据时有时会丢失数据。 它从
    发表于 07-07 08:16

    主流版本控制工具Git vs Perforce P4:架构模式、性能、大文件管理及分支管理对比详解

    Git vs Perforce P4,如何选型?架构模式、性能、大文件管理、分支策略四大维度对比,帮你全面了解两者的核心差异,选择更合适你团队需求的版本控制系统。
    的头像 发表于 06-13 14:52 575次阅读
    主流版本控制工具Git vs Perforce P4:架构模式、性能、<b class='flag-5'>大文件</b>管理及分支管理对比详解

    基于RK3576开发板的INI文件操作

    ”的表达方式为[Section1 Name],以括号“[]”包围起来,它用来表示一个段落的开始,因INI文件可能是整个项目共用的,所以需要使用“节”来区分不同用途的参数区。“键”与“值”的组合表达方式为
    的头像 发表于 05-12 14:47 1364次阅读
    基于RK3576开发板的INI<b class='flag-5'>文件</b><b class='flag-5'>操作</b>

    鸿蒙文件传输三方库上线开源鸿蒙社区 十行代码实现大文件高速传输

    、断点续下、分片上传、断点续传、自动重试等多个特性的高性能文件传输解决方案,让开发者开箱即用,轻松实现高效稳定的文件传输功能。 在应用开发过程,许多场景涉及到大文件的传输,尤其是在
    发表于 03-06 10:29

    Java的SPI机制详解

    接口规范以及可以发现接口实现的机制,而不需要实现接口。 SPI机制在Java应用广泛。例如:JDBC的数据库连接驱动使用SPI机制,只定义了数据库连接接口的规范,而具体实现由各大数据库厂商实现,不同数据库的实现不同,我们常用
    的头像 发表于 03-05 11:35 1104次阅读
    <b class='flag-5'>Java</b>的SPI机制详解

    hyper-v 文件,Hyper-V文件管理:高效操作指南

    在日常办公,我们常常需要对大量文件或数据进行重复性操作,比如批量修改文件名、批量更新数据等。这些任务不仅耗时费力,还容易出错。幸运的是,批量管理工具的出现为我们提供了一种高效的解决方
    的头像 发表于 02-06 10:27 1262次阅读
    hyper-v <b class='flag-5'>文件</b>,Hyper-V<b class='flag-5'>文件</b>管理:高效<b class='flag-5'>操作</b>指南

    hyper-v文件,hyper-v文件查找的正确步骤和操作方法是什么?

    在使用Hyper-V进行虚拟化操作时,准确找到相关文件至关重要。无论是需要对虚拟机进行备份、迁移,还是对其配置进行修改,都离不开对Hyper-V文件的查找。那么,查找Hyper-V文件
    的头像 发表于 01-24 14:40 1620次阅读
    hyper-v<b class='flag-5'>文件</b>,hyper-v<b class='flag-5'>文件</b>查找的正确步骤和<b class='flag-5'>操作</b>方法是什么?

    Spire.XLS for Android via Java组件说明

    Spire.XLS for Android via Java 是一款专业的 Android Excel 组件,用于在 Android 手机应用程序创建、操作和转换 Excel 工作表,并且运行环境
    的头像 发表于 01-24 12:16 813次阅读
    Spire.XLS for Android via <b class='flag-5'>Java</b>组件说明

    SSM框架在Java开发的应用 如何使用SSM进行web开发

    SSM框架,即Spring、SpringMVC和MyBatis的整合,是Java Web开发中常用的技术栈。它通过分层架构,实现了视图、控制、业务逻辑和数据访问的分离,提高了代码的可维护性和可扩展性
    的头像 发表于 12-16 17:28 2095次阅读