Java 内存映射读取文件_Java内存映射 大文件轻松处理|chu

前言

内存映射文件(Memory-mapped File),指的是将一段虚拟内存逐字节映射于一个文件,使得应用程序处理文件如同访问主内存(但在真正使用到这些数据前却不会消耗物理内存,也不会有读写磁盘的操作),这要比直接文件读写快几个数量级。

稍微解释一下虚拟内存(很明显,不是物理内存),它是计算机系统内存管理的一种技术。像施了妖法一样使得应用程序认为它拥有连续的可用的内存,实际上呢,它通常是被分隔成多个物理内存的碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。

内存映射文件主要的用处是增加 I/O 性能,特别是针对大文件。对于小文件,内存映射文件反而会导致碎片空间的浪费,因为内存映射总是要对齐页边界,最小单位是 4 KiB,一个 5 KiB 的文件将会映射占用 8 KiB 内存,也就会浪费 3 KiB 内存。

java.nio 包使得内存映射变得非常简单,其中的核心类叫做 MappedByteBuffer,字面意思为映射的字节缓冲区。

01、使用 MappedByteBuffer 读取文件

假设现在有一个文件,名叫 cmower.txt,里面的内容是:

沉默王二,一个有趣的程序员

PS:哎,改不了王婆卖瓜自卖自夸这个臭毛病了,因为文章被盗得都怕了。

这个文件放在 /resource 目录下,我们可以通过下面的方法获取到它:

ClassLoader classLoader = Cmower.class.getClassLoader(); Path path = Paths.get(classLoader.getResource("cmower.txt").getPath());

Path 既可以表示一个目录,也可以表示一个文件,就像 File 那样――当然了,Path 是用来取代 File 的。

然后,从文件中获取一个 channel(通道,对磁盘文件的一种抽象)。

FileChannel fileChannel = FileChannel.open(path);

紧接着,调用 FileChannel 类的 map 方法从 channel 中获取 MappedByteBuffer,此类扩展了 ByteBuffer――提供了一些内存映射文件的基本操作方法。

MappedByteBuffer mappedByteBuffer = fileChannel.map(mode, position, size);

稍微解释一下 map 方法的三个参数。

1)mode 为文件映射模式,分为三种:MapMode.READ_ONLY(只读),任何试图修改缓冲区的操作将导致抛出 ReadOnlyBufferException 异常。

MapMode.READ_WRITE(读/写),任何对缓冲区的更改都会在某个时刻写入文件中。需要注意的是,其他映射同一个文件的程序可能不能立即看到这些修改,多个程序同时进行文件映射的行为依赖于操作系统。

MapMode.PRIVATE(私有), 对缓冲区的更改不会被写入到该文件,任何修改对这个缓冲区来说都是私有的。

2)position 为文件映射时的起始位置。

3)size 为要映射的区域的大小,必须是非负数,不得大于Integer.MAX_VALUE。

一旦把文件映射到内存缓冲区,我们就可以把里面的数据读入到 CharBuffer 中并打印出来。具体的代码示例如下。

CharBuffer charBuffer = null; ClassLoader classLoader = Cmower.class.getClassLoader(); Path path = Paths.get(classLoader.getResource("cmower.txt").getPath()); try (FileChannel fileChannel = FileChannel.open(path)) { MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_ONLY, 0, fileChannel.size()); if (mappedByteBuffer != null) { charBuffer = Charset.forName("UTF-8").decode(mappedByteBuffer); } System.out.println(charBuffer.toString()); } catch (IOException e) { e.printStackTrace(); }

由于 decode() 方法的参数是 MappedByteBuffer,这就意味着我们是从内存中而不是磁盘中读入的文件内容,所以速度会非常快。

02、使用 MappedByteBuffer 写入文件

假设现在要把下面的内容写入到一个文件,名叫 cmower1.txt。

沉默王二,《Web全栈开发进阶之路》作者

这个文件还没有创建,计划放在项目的 classpath 目录下。

Path path = Paths.get("cmower1.txt");

具体位置见下图所示。

20190926_5d8c3e310f4d5.png

然后,创建文件的通道。

FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)

仍然使用的 open 方法,不过增加了 3 个参数,前 2 个很好理解,表示文件可读(READ)、可写(WRITE);第 3 个参数 TRUNCATE_EXISTING 的意思是如果文件已经存在,并且文件已经打开将要进行 WRITE 操作,则其长度被截断为 0。

紧接着,仍然调用 FileChannel 类的 map 方法从 channel 中获取 MappedByteBuffer。

MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_WRITE, 0, 1024);

这一次,我们把模式调整为 MapMode.READ_WRITE,并且指定文件大小为 1024,即 1KB 的大小。然后使用 MappedByteBuffer 中的 put() 方法将 CharBuffer 的内容保存到文件中。具体的代码示例如下。

CharBuffer charBuffer = CharBuffer.wrap("沉默王二,《Web全栈开发进阶之路》作者"); Path path = Paths.get("cmower1.txt"); try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) { MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_WRITE, 0, 1024); if (mappedByteBuffer != null) { mappedByteBuffer.put(Charset.forName("UTF-8").encode(charBuffer)); } } catch (IOException e) { e.printStackTrace(); }

可以打开 cmower1.txt 查看一下内容,确认预期的内容有没有写入成功。

03、MappedByteBuffer 的遗憾

据说,在 Java 中使用 MappedByteBuffer 是一件非常麻烦并且痛苦的事,主要表现有:

1)一次 map 的大小最好限制在 1.5G 左右,重复 map 会增加虚拟内存回收和重新分配的压力。也就是说,如果文件大小不确定的话,就不太友好。

2)虚拟内存由操作系统来决定什么时候刷新到磁盘,这个时间不太容易被程序控制。

3)MappedByteBuffer 的回收方式比较诡异。

再次强调,这三种说法都是据说,我暂时能力有限,也不能确定这种说法的准确性,很遗憾。

04、比较文件操作的处理时间

嗨,朋友,阅读完以上的内容之后,我想你一定对内存映射文件有了大致的了解。但我相信,如果你是一名负责任的程序员,你一定还想知道:内存映射文件的读取速度究竟有多快。

为了得出结论,我叫了另外三名竞赛的选手:InputStream(普通输入流)、BufferedInputStream(带缓冲的输入流)、RandomAccessFile(随机访问文件)。

读取的对象是加勒比海盗4惊涛怪浪.mkv,大小为 1.71G。

1)普通输入流

public static void inputStream(Path filename) { try (InputStream is = Files.newInputStream(filename)) { int c; while((c = is.read()) != -1) { } } catch (IOException e) { e.printStackTrace(); } }

2)带缓冲的输入流

public static void bufferedInputStream(Path filename) { try (InputStream is = new BufferedInputStream(Files.newInputStream(filename))) { int c; while((c = is.read()) != -1) { } } catch (IOException e) { e.printStackTrace(); } }

3)随机访问文件

public static void randomAccessFile(Path filename) { try (RandomAccessFile randomAccessFile = new RandomAccessFile(filename.toFile(), "r")) { for (long i = 0; i < randomAccessFile.length(); i++) { randomAccessFile.seek(i); } } catch (IOException e) { e.printStackTrace(); } }

4)内存映射文件

public static void mappedFile(Path filename) { try (FileChannel fileChannel = FileChannel.open(filename)) { long size = fileChannel.size(); MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_ONLY, 0, size); for (int i = 0; i < size; i++) { mappedByteBuffer.get(i); } } catch (IOException e) { e.printStackTrace(); } }

测试程序也很简单,大致如下:

long start = System.currentTimeMillis(); bufferedInputStream(Paths.get("jialebi.mkv")); long end = System.currentTimeMillis(); System.out.println(end-start);

四名选手的结果如下表所示。方法时间普通输入流龟速,没有耐心等出结果

随机访问文件龟速,没有耐心等下去

带缓冲的输入流29966

内存映射文件914

普通输入流和随机访问文件都慢得要命,真的是龟速,我没有耐心等待出结果;带缓冲的输入流的表现还不错,但相比内存映射文件就逊色多了。由此得出的结论就是:内存映射文件,上G大文件轻松处理。

05、最后

本篇文章主要介绍了 Java 的内存映射文件,MappedByteBuffer 是其灵魂,读取速度快如火箭。另外,所有这些示例和代码片段都可以在 GitHub 上找到――这是一个 Maven 项目,所以它很容易导入和运行。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持华域联盟。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/445800.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode - Easy - 118. Pascal‘s Triangle

Topic Array Description https://leetcode.com/problems/pascals-triangle/ Given a non-negative integer numRows, generate the first numRows of Pascal’s triangle. In Pascal’s triangle, each number is the sum of the two numbers directly above it. Example…

LeetCode - Easy - 119. Pascal‘s Triangle II

Topic Array Description https://leetcode.com/problems/pascals-triangle-ii/ Given an integer rowIndex, return the rowIndexth row of the Pascal’s triangle. Notice that the row index starts from 0. In Pascal’s triangle, each number is the sum of the tw…

java原始模型模式_java设计模式--原始模型模式

简介原始模型模式属于对象的创建模式。通过一个原型对象来指明要创建对象的类型&#xff0c;然后用复制原型对象的方法来创建出更多同类型的对象。Java所有的类都是从java.lang.Object类继承来的&#xff0c;Object类提供clone()方法对对象进行复制。一般调用clone()方法需要满…

java并行计算同步返回_Java大文本并行计算实现过程解析

Java大文本并行计算实现过程解析简单提高文本读取效率&#xff0c;使用BufferedReader是个不错的选择。速度最快的方法是MappedByteBuffer&#xff0c;但是&#xff0c;相比BufferedReader而言&#xff0c;效果不是非常明显。也就是说&#xff0c;后者虽然快&#xff0c;但也快…

java mvc 菜鸟_【java框架】SpringMVC(1)--SpringMVC入门

1.SpringMVC框架认识Spring MVC是一个基于MVC模式的Web框架&#xff0c;SpringMVC作为Spring中的一个模块&#xff0c;它与Spring能够无缝集成&#xff0c;主要用于解决企业Web开发中常见的问题&#xff1a;如参数接收、文件上传、表单验证、国际化等等。2.SpringMVC HelloWorl…

php设置cookie 域名,php如何设置cookie对整个域名有效?

php设置cookie对整个域名有效的方法&#xff1a;由setcookie函数让cookie对整个域名有效&#xff0c;代码为【setcookie("cookie_test", this is cookie test, time()3600,"/",“】。php设置cookie对整个域名有效的方法&#xff1a;默认情况下的cookie仅对…

我的世界一个程序导致JAVA,Java地位无可动摇的12个原因

如今&#xff0c;面对曾经在程序员中被各种新技术掩盖直至堙灭的技术值得怀念。犹如COBOL这当年被老程序员们尊为神器的语言如今也基本没有价值。而Java作为现代程序员的中坚力量在这点上或许会成为下一个COBOL。有关JAVA的技术卖出多少本书已经是一个很久远的记忆了。现处中年…

mysql 5.5.23 winx64,win10下mysql 5.7.23 winx64安装配置方法图文教程

本文记录了mysql 5.7.23 winx64安装教程&#xff0c;具体内容如下Step1选择手动下载版本解压到自己指定的路径上图中的my.ini及data文件夹在压缩包里是没有的&#xff0c;后面需要自己添加my.ini如下&#xff0c;直接copy~然后在目录下创建一个data文件夹Step2设置环境变量电脑…

matlab生成均匀部分散点图,应用halton序列生成均匀散点图

前言一门课的作业要用RBF-DQ方法计算流场。简单来讲就是要在无网格的条件下用高精度格式实现流场的仿真计算。生成散点图时&#xff0c;用蒙特卡洛的方法得到的散点图分布不是很均匀&#xff0c;于是想到一种叫做halton的序列。本篇将运用halton序列生成均匀的散点图。halton序…

解决动态规划问题4步曲

概述 &#xff08;确定状态&#xff09;确定问题状态 提炼最后一步子问题转化 &#xff08;求得方程&#xff09;转移方程&#xff0c;把问题方程化&#xff08;设初置界&#xff09;按照实际逻辑设置初始条件和边界情况&#xff08;确序再解&#xff09;确定计算顺序并求解 …

查看oracle监听服务器,处理Oracle 监听文件listener.log问题

如果连接时候变得较慢 查看Oracle日志记录&#xff0c;可能是因为此文件太大&#xff0c;超过2G&#xff0c;需要定期清理&#xff0c;(如果多用户&#xff0c;记得用root&#xff0c;可能没权限)查看listener.log&#xff1f;find / -name listener.log经查看&#xff0c;竟然…

美国oracle球场,美国体育馆考察——美国体育产业是如何盈利的?

体育是美国一项较高利润的产业&#xff0c;其发展规模、发展水平和效益都是世界一流的。美国体育馆考察&#xff0c;主要考察美国体育产业的盈利模式和体育赛事的赞助模式以及球馆的运营管理&#xff0c;并对比中美体育产业的差异&#xff0c;从中获得先进的体育产业运营思维&a…

php集成环境怎么打开,PHP集成开发环境PhpStorm快速入门指南(二):打开一个项目...

PhpStorm是一个轻量级且便捷的PHP IDE&#xff0c;其旨在提高用户效率&#xff0c;可深刻理解用户的编码&#xff0c;提供智能代码补全&#xff0c;快速导航以及即时错误检查。可随时帮助用户对其编码进行调整&#xff0c;运行单元测试或者提供可视化debug功能。PhpStorm 2019.…

计算机分php,计算机按照处理数据的形态分类,可以分为什么?

计算机按照处理数据的形态分类&#xff0c;可以分为&#xff1a;1、数字计算机&#xff0c;是以数字形式的量值在机器内部进行运算和存储的电子计算机&#xff1b;2、模拟计算机&#xff0c;是根据相似原理&#xff0c;用一种连续变化的模拟量作为被运算的对象的计算机&#xf…

2.oracle物理结构,oracle实验2oracle物理结构管理

oracle实验2oracle物理结构管理 (6页)本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01;9.9 积分实验2 oracle物理存储结构管理、实验目的1. 掌握物理结构的创建和修改方法2. 掌握表空间的存储参数设置方…

linux mount 查看挂载目录,Linux下使用mount来挂载设备到目录

一般情况下直接mount 设备路径 目录路径&#xff0c;就可以了。umount 设备名&#xff0c;就可以卸载这个设备了使用lsblk -f可以查看挂载的设备&#xff0c;以及这些设备的文件系统。roottao-PC:/boot# lsblk -fNAME FSTYPE LABEL UUID MOUNTPOINTsda├─sda1├─sda2 vfat SY…

linux6.0 安装教程,CentOS 6.0安装步骤

1&#xff0e;安装引导选择安装或升级现有系统(Install or upgrade an existing system)&#xff1a;这个选项是默认的。 选择此选项&#xff0c;安装到您的计算机使用CentOS的图形安装程序的系统。2.检测光盘介质可以选择skip跳过3.选择安装过程中的语言这里选择chinese中文简…

netflow流量分析工具 linux,Centos5/Linux安装Nfdump和Nfsen图形界面分析netflow数据

Nfdump是linux下netflow数据采集分析工具&#xff0c;Nfsen是基于nfdump是web界面工具&#xff0c;服务器需先安装web服务器和php环境。安装rrdtool及所需组件&#xff1a;yum install perl-rrdtool rrdtool rrdtool-devel rrdutils flex byacc安装所需perl模块&#xff1a;yum…

linux嵌入式平台测试,protobuf-c 在arm linux 嵌入式平台的使用 测试

关于什么是protobuf&#xff0c;网上搜搜一大堆&#xff0c;很多人用的都还是json&#xff0c;以为json是多种语言传输数据是万能的&#xff0c;看完了protobuf的实现&#xff0c;就明白了简单高效才是王道。1、首先写一个.proto扩展名的文件json.proto&#xff0c;内容格式如下…

linux版车机安装步骤,RedHat Linux 9.0的安装(详细图解安装过程)

RedHat Linux版本:" b, t) b) b# }, t# z- fC& S$ x0 }) GRedHat Linux是目前世界上使用最多的Linux操作系统。因为它具备最好的图形界面&#xff0c;无论是安装、配置还是使用都十分方便&#xff0c;而且运行稳定&#xff0c;因此不论是新手还是老玩家都对它有很高的…