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.*

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

    关注

    8

    文章

    3034

    浏览量

    74128
  • JAVA
    +关注

    关注

    19

    文章

    2971

    浏览量

    104845
收藏 人收藏

    评论

    相关推荐

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

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

    如何使用SFTP传输大文件

    在当今的数字化时代,大文件传输变得越来越常见。无论是企业数据迁移、远程备份还是内容分发,都需要一种既安全又高效的文件传输方式。SFTP作为一种基于SSH的文件传输协议,提供了一种加密的传输方式,确保
    的头像 发表于 11-13 14:11 773次阅读

    Windows连接P2Link的FTP服务——远程操作文件

    FTP协议允许用户通过客户端软件连接到FTP服务器,进行文件的上传、下载、删除、重命名等操作。P2Link可快速为本地文件目录提供公网FTP服务地址,用于远程
    的头像 发表于 11-13 11:48 259次阅读
    Windows连接P2Link的FTP服务——远程<b class='flag-5'>操作文件</b>

    Java时间戳的使用

    Java时间戳的使用
    的头像 发表于 11-06 16:04 230次阅读
    <b class='flag-5'>Java</b><b class='flag-5'>中</b>时间戳的使用

    P2Link是什么?——免费让你体验高效智联的新方式

    P2Link 是一个非常方便、简单的工具,可以帮你直接传输大文件,或者让别人访问你内网的服务。它不像传统工具需要复杂的配置,也没有上传到云端的烦恼。特别适合那些临时共享文件、传大文件
    的头像 发表于 10-31 14:11 221次阅读

    怎么在JAVA确定线性池大小

    JAVA确定线性池大小,分别介绍CPU密集型任务和I/O密集型任务及其处理方法。
    的头像 发表于 10-24 14:02 196次阅读

    java反编译能拿到源码吗

    Java反编译是一种将编译后的Java字节码(.class文件)转换回Java源代码的过程。虽然反编译可以帮助理解代码的逻辑和结构,但它并不总是能完美地还原原始源代码。反编译工具通常会
    的头像 发表于 09-02 11:03 1054次阅读

    如何修改buildroot和debian文件系统

    本文档主要介绍在没有编译环境的情况下,如何修改buildroot和debian文件系统方法,如在buildroot文件系统添加文件、修改目录等文件
    的头像 发表于 07-22 17:46 510次阅读
    如何修改buildroot和debian<b class='flag-5'>文件</b>系统

    如何实现Python复制文件操作

    Python 中有许多“开盖即食”的模块(比如 os,subprocess 和 shutil)以支持文件 I/O 操作。在这篇文章,你将会看到一些用 Python 实现文件复制的特殊
    的头像 发表于 07-18 14:53 435次阅读

    华纳云:java web和java有什么区别java web和java有什么区别

    的平台,Java可以用于开发桌面应用程序、移动应用程序、企业级应用程序等。 – Java Web是Java语言在Web开发领域的应用,它使用Java威廉希尔官方网站 来构建动态的Web应用程序,这些
    的头像 发表于 07-16 13:35 831次阅读
    华纳云:<b class='flag-5'>java</b> web和<b class='flag-5'>java</b>有什么区别<b class='flag-5'>java</b> web和<b class='flag-5'>java</b>有什么区别

    esp32如何一次性读取大文件数据?

    esp32没有提供数据库读写的例子,最近有个大文件,无法一次性读出,请问,怎么读取,json中一部分json数组。然后修改完了以后,在写入进去?
    发表于 06-25 06:52

    使用ESP32-S3开发板http post请求发送SD卡上的大文件,如何循环边读取文件边分块发送文件呢?

    您和,我准备使用ESP32-S3开发板http post请求发送SD卡上的大文件,但是使用esp_http_client_set_post_field的buffer太小,内存不能一次性申请太大,请问
    发表于 06-06 06:19

    Oracle确认Java/JDK 11官方支持延长至2032年1月 

    此外,Solaris操作系统上的Java SE 8和Java SE 11的官方支持也同步延期至2030年12月及2032年1月,进一步延长了该平台上的Java服务周期。
    的头像 发表于 05-16 15:57 1295次阅读

    蔚来宣布完成NIO Phone 2研发,每年仅发布一款新机

    值得注意的是,早在2023年9月的蔚来创新科技日,李斌曾在接受媒体采访时透露,蔚来手机会持续迭代,研发周期已形成“固定模式”,第二代NIO Phone正处于研发当中。而在同年12月,蔚来自家推出首款智能手机——蔚来NIO Phone。
    的头像 发表于 03-19 15:40 1352次阅读

    java实现多线程的几种方式

    Java实现多线程的几种方式 多线程是指程序包含了两个或以上的线程,每个线程都可以并行执行不同的任务或操作Java的多线程可以提高程序
    的头像 发表于 03-14 16:55 736次阅读