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…

jenv java_mac 上使用jenv 管理的多个java 版本

由于服务器是java1.7&#xff0c; mac上是1.8&#xff0c;因此mac编译的java代码会在服务器上报错。因此&#xff0c;需要修改mac上java版本&#xff0c;自己折腾了很久&#xff0c;放弃&#xff0c;决定使用jenv 管理&#xff01; 结果是非常方便使用步骤&#xff1a;1、安装 …

mysql 源码 缓存_MySQL源码:MYSQL存储过程/函数的分析原理及缓存机制

前言&#xff1a;我个人认为&#xff0c;有关MYSQL存储过程/函数在MYSQL中的实现比较粗糙&#xff0c;可扩展性不够好&#xff0c;其实现的耦合性太高&#xff0c;所以主要讲一些它的原理方面的内容&#xff0c;但有可能在某些方面理解不够好或者有些不正确的地方&#xff0c;欢…

如何单元测试Java的private方法

问题 Java类中private方法通常只能被其所属类的调用&#xff0c;其他类只能望而却步&#xff0c;单元测试private方法也就一筹莫展。 尝试解法&#xff1a; 在测试时&#xff0c;手动将private改为public&#xff0c;测试完后再将其改回。将测试方法写进private方法的所属类…

图论与java_算法笔记_150:图论之双连通及桥的应用(Java)

1 问题描述DescriptionIn order to get from one of the F (1 < F < 5,000) grazing fields (which are numbered 1..F) to another field, Bessie and the rest of the herd are forced to cross near the Tree of Rotten Apples. The cows are now tired of often bein…

如何用JUnit单元测试List

问题 JUnit测试List时差强人意。 解法 引入依赖 hamcrest-library包含许多有用方法来测试List数据类型。 <dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version>&l…

java数据包解析_请教http请求数据包如何解析 重组

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼下面是我捕获到的请求报文片段dst_ip:/121.52.228.134ack:trueack_num:3064957366date:POST /messagebroker/amf HTTP/1.1Host: s16.xxhzw.game.yy.comUser-Agent: Mozilla/5.0 (Windows NT 5.1; rv:13.0) Gecko/20100101 Firefox/…

webqq java_WebQQ登录详解

第二次登录请求方式:POST地址:http://d.web2.qq.com/channel/login2POST正文:r%7B%22status%22%3A%22online%22%2C%22ptwebqq%22%3A%22{0}%22%2C%22passwd_sig%22%3A%22%22%2C%22clientid%22%3A%22{1}%22%2C%22  psessionid%22%3Anull%7D&clientid{2}&psessionidnull…

LeetCode - Easy - 155. Min Stack

Topic StackDesign Description https://leetcode.com/problems/min-stack/ Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. push(x) – Push element x onto stack.pop() – Removes the element on top of the st…

java judgefilecode_VScode出现无法打开“X”: 找不到文件(file:///XXXX) 的解决办法

如标题&#xff0c;被这个问题整了好长时间了&#xff0c;调试的时候如果有语法错误只能显示相应的的行数&#xff0c;没有办法定位到出错的行数上。(由于用处不是很大并且没有找到解决办法&#xff0c;所以就一直放着没管23333)直到最近看到一位大佬的解决办(重写正则表达式)法…

LeetCode - Easy - 169. Majority Element

Topic ArrayDivide and ConquerBit Manipulation Description https://leetcode.com/problems/majority-element/ Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times. You may assume t…

java 静态方法 异常_java空指针异常与静态方法

从一道经典面试题说起&#xff0c;public class HaHa {public static void haha(){System.out.println("haha");}public static void main(String[] args){((HaHa)null).haha();}}打印结果 haha。这段题考查两点知识&#xff0c;java的空指针异常和静态方法。1&#…

java中的asList_Java中的Arrays.asList()方法

Arrays.asList()返回一个List&#xff0c;但是这种情况下&#xff0c;其底层的实现是一个final数组&#xff0c;因此不能调整其尺寸如下代码片段&#xff1a;package chapter11.t1;import java.util.*;public class AddingGroups {public static void main(String[] args) {Lis…

java控制面板作用_Java

1. JAVA 的特性和优势(1) Java的核心优势 跨平台/可移植性(2) 其他特性 安全性&#xff1b;面对对象&#xff1b;简单性&#xff1b;高性能&#xff1b;分布式&#xff1b;多线程&#xff1b;健壮性&#xff1b;① 强大的生态系统(3) Java与C的关系 Java是C的简化版(C—)2. JAV…

java es 数据批量导入_ElasticSearch—Java批量导入导出

网上找了很多&#xff0c;我的es是2.3.5版本&#xff0c;网上的客户端最少都是5.x版本&#xff0c;所以没有能用的。自己整合了一下 2.3.5版本的。pom文件&#xff1a;org.elasticsearchelasticsearch2.3.5com.alibabafastjson1.1.35org.apache.commonscommons-io1.3.2org.apac…

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

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

Windows的命令行窗口运行Python时,如何清屏?

问题 如标题 解法 import os os.system("cls")参考 python实现清屏

手写文字识别java_java 手写文字图片识别提取 百度API

package org.fh.util;import org.json.JSONObject;import java.io.BufferedReader;import java.io.InputStreamReader;import java.net.HttpURLConnection;import java.net.URL;import java.util.List;import java.util.Map;/*** 说明&#xff1a;获取文字识别token类* from&am…

LeetCode - Easy - 191. Number of 1 Bits

Topic Bit Manipulation Description https://leetcode.com/problems/number-of-1-bits/ Write a function that takes an unsigned integer and returns the number of ‘1’ bits it has (also known as the Hamming weight). Note: Note that in some languages such …