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

    文章

    2763

    浏览量

    72749
  • JAVA
    +关注

    关注

    19

    文章

    2904

    浏览量

    102989
收藏 人收藏

    评论

    相关推荐

    java环境配置成功后怎么运行

    Java环境配置成功后,我们可以使用几种方式来运行Java程序。下面将详细介绍这几种方式以及其使用方法。 命令行运行方式 在成功配置Java环境后,我们可以通过命令行来运行
    的头像 发表于 12-06 15:57 530次阅读

    java环境配置成功后能执行哪些操作

    Java环境配置成功后,您可以进行以下操作: 编写和运行Java程序:配置成功后,您可以使用Java编写程序,运行并测试它们。您可以使用任何文本编辑器编写
    的头像 发表于 12-06 15:55 318次阅读

    idea要配置java环境变量吗

    环境变量的步骤及其重要性。 在开始之前,我们先了解一下环境变量的概念。环境变量是操作系统定义的一些值,用于指定操作系统及其应用程序如何运行。通过设置环境变量,我们可以指定操作系统查找可
    的头像 发表于 12-06 14:02 390次阅读

    java程序必须以什么为文件扩展名

    “一次编写,到处运行”的原则。 Java程序通常以.java文件扩展名。在编码过程中,开发者需要使用文本编辑器(如Notepad++、Sublime Text等)来编写Java代码。
    的头像 发表于 11-29 14:24 402次阅读

    java如何清空obj文件内容

    清空一个文件的内容可以通过以下步骤来完成。 首先,你需要指定要清空的文件的路径。你可以使用Java的File类来操作文件。以下是一个使用Fi
    的头像 发表于 11-21 10:29 223次阅读

    如何用Rust通过JNI和Java进行交互

    近期工作中有Rust和Java互相调用需求,这篇文章主要介绍如何用Rust通过JNI和Java进行交互,还有记录一下开发过程中遇到的一些坑。
    的头像 发表于 10-17 11:41 390次阅读

    Java序列化怎么使用

    java 对象经常需要在网络中以 socket 传输或者需要保存到文件中。这时不管 java 对象是文件、数据、图像还是其他格式,都可以转换为一个 byte[] 数组保存到
    的头像 发表于 10-10 14:19 244次阅读

    Java不同的算法

    在本文中,我们将讨论使用 Java 验证一个给定的字符串是否具有操作系统的有效文件名的不同方法。我们可以根据限制的字符或长度限制来检查该值。 我们将只关注核心解决方案,不使用任何外部依赖。我们将使
    的头像 发表于 10-08 11:43 463次阅读

    IO与NIO有何区别

    NIO 提到IO,这是Java提供的一套类库,用于支持应用程序与内存、文件、网络间进行数据交互,实现数据写入与输出。JDK自从1.4版本后,提供了另一套类库NIO,我们平时习惯称呼为N
    的头像 发表于 09-25 11:00 410次阅读
    IO与<b class='flag-5'>NIO</b>有何区别

    大文件传输的9种方法

    SendBig是在全球范围内发送你的文件的最简单方法。免费分享高达30GB的大文件、照片和视频,让文件共享变得简单。在不注册的情况下,可以发送高达50MB的数据,如果你想发送更大的文件
    的头像 发表于 07-29 11:12 2846次阅读
    <b class='flag-5'>大文件</b>传输的9种方法

    Linux平台大文件生成和处理方法

    在日常工作中,为了验证某些场景下的功能,经常需要人为构造一些大文件进行测试,有时需要用大文件来测试下载速度,有时需要用大文件来覆盖磁盘空间;偶尔会看到一些网络博文会教大家如何构造大文件
    发表于 07-14 16:38 3450次阅读

    一款解决大文件内存溢出的 Excel 处理工具

      介绍 快速开始 引入依赖 简单导出 定义实体类 复杂导出 简单导入 参考资料 介绍 EasyExcel 是一个基于 Java 的、快速、简洁、解决大文件内存溢出的 Excel 处理工具。它能
    的头像 发表于 07-03 16:11 1107次阅读
    一款解决<b class='flag-5'>大文件</b>内存溢出的 Excel 处理工具

    如何从Linux内核角度探秘Java NIO文件并读写本质呢?

    又在此基础之上介绍了针对 socket 文件的相关操作及其对应在内核中的处理流程:
    的头像 发表于 06-12 14:14 533次阅读
    如何从Linux内核角度探秘<b class='flag-5'>Java</b> <b class='flag-5'>NIO</b><b class='flag-5'>文件</b>并读写本质呢?

    如何将.txt 大文件发送到HTTP服务器?

    我在 SPIFFS 闪存创建了多个 .txt 文件系统。但是如何将 .txt 大文件发送到 HTTP 服务器,可以通过 ESP8266 12F 将 SPIFFS 1MB
    发表于 05-19 09:00

    如何通过python轻松处理大文件

    众所周知,python除了以简洁著称,其成熟的第三方库功能也是很强大的,今天浩道带大家看看如何通过python轻松处理大文件,真让人直呼yyds 。
    的头像 发表于 04-27 10:54 493次阅读