一般高性能的涉及到存储框架,例如 RocketMQ,Kafka 这种消息队列,存储日志的时候,都是通过 Java File MMAP 实现的,那么什么是 Java File MMAP 呢?

什么是 Java File MMAP

尽管从JDK 1.4版本开始,Java 内存映射文件(Memory Mapped Files)就已经在java.nio包中,但它对很多程序开发者来说仍然是一个相当新的概念。引入 NIO 后,Java IO 已经相当快,而且内存映射文件提供了 Java 有可能达到的最快 IO 操作,这也是为什么那些高性能 Java 应用应该使用内存映射文件来持久化数据。

作为 NIO 的一个重要的功能,MMAP 方法为我们提供了将文件的部分或全部映射到内存地址空间的能力,同当这块内存区域被写入数据之后会变成脏页,操作系统会用一定的算法把这些数据写入到文件中,而我们的 Java 程序不需要去关心这些。这就是内存映射文件的一个关键优势,即使你的程序在刚刚写入内存后就挂了,操作系统仍然会将内存中的数据写入文件系统。

另外一个更突出的优势是共享内存,内存映射文件可以被多个进程同时访问,起到一种低时延共享内存的作用。

Java File MMAP 与直接操作文件性能对比

package com.github.hashZhang.scanfold.jdk.file;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Random; public class FileMmapTest {
public static void main(String[] args) throws Exception {
//记录开始时间
long start = System.currentTimeMillis();
//通过RandomAccessFile的方式获取文件的Channel,这种方式针对随机读写的文件较为常用,我们用文件一般是随机读写
RandomAccessFile randomAccessFile = new RandomAccessFile("./FileMmapTest.txt", "rw");
FileChannel channel = randomAccessFile.getChannel();
System.out.println("FileChannel初始化时间:" + (System.currentTimeMillis() - start) + "ms"); //内存映射文件,模式是READ_WRITE,如果文件不存在,就会被创建
MappedByteBuffer mappedByteBuffer1 = channel.map(FileChannel.MapMode.READ_WRITE, 0, 128 * 1024 * 1024);
MappedByteBuffer mappedByteBuffer2 = channel.map(FileChannel.MapMode.READ_WRITE, 0, 128 * 1024 * 1024); System.out.println("MMAPFile初始化时间:" + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis();
testFileChannelSequentialRW(channel);
System.out.println("FileChannel顺序读写时间:" + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis();
testFileMMapSequentialRW(mappedByteBuffer1, mappedByteBuffer2);
System.out.println("MMAPFile顺序读写时间:" + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis();
try {
testFileChannelRandomRW(channel);
System.out.println("FileChannel随机读写时间:" + (System.currentTimeMillis() - start) + "ms");
} finally {
randomAccessFile.close();
} //文件关闭不影响MMAP写入和读取
start = System.currentTimeMillis();
testFileMMapRandomRW(mappedByteBuffer1, mappedByteBuffer2);
System.out.println("MMAPFile随机读写时间:" + (System.currentTimeMillis() - start) + "ms");
} public static void testFileChannelSequentialRW(FileChannel fileChannel) throws Exception {
byte[] bytes = "测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1".getBytes();
byte[] to = new byte[bytes.length];
//分配直接内存,减少复制
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bytes.length);
//顺序写入
for (int i = 0; i < 100000; i++) {
byteBuffer.put(bytes);
byteBuffer.flip();
fileChannel.write(byteBuffer);
byteBuffer.flip();
} fileChannel.position(0);
//顺序读取
for (int i = 0; i < 100000; i++) {
fileChannel.read(byteBuffer);
byteBuffer.flip();
byteBuffer.get(to);
byteBuffer.flip();
}
} public static void testFileMMapSequentialRW(MappedByteBuffer mappedByteBuffer1, MappedByteBuffer mappedByteBuffer2) throws Exception {
byte[] bytes = "测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2".getBytes();
byte[] to = new byte[bytes.length]; //顺序写入
for (int i = 0; i < 100000; i++) {
mappedByteBuffer1.put(bytes);
}
//顺序读取
for (int i = 0; i < 100000; i++) {
mappedByteBuffer2.get(to);
}
} public static void testFileChannelRandomRW(FileChannel fileChannel) throws Exception {
try {
byte[] bytes = "测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1测试字符串1".getBytes();
byte[] to = new byte[bytes.length];
//分配直接内存,减少复制
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bytes.length);
//随机写入
for (int i = 0; i < 100000; i++) {
byteBuffer.put(bytes);
byteBuffer.flip();
fileChannel.position(new Random(i).nextInt(bytes.length*100000));
fileChannel.write(byteBuffer);
byteBuffer.flip();
}
//随机读取
for (int i = 0; i < 100000; i++) {
fileChannel.position(new Random(i).nextInt(bytes.length*100000));
fileChannel.read(byteBuffer);
byteBuffer.flip();
byteBuffer.get(to);
byteBuffer.flip();
}
} finally {
fileChannel.close();
}
} public static void testFileMMapRandomRW(MappedByteBuffer mappedByteBuffer1, MappedByteBuffer mappedByteBuffer2) throws Exception {
byte[] bytes = "测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2测试字符串2".getBytes();
byte[] to = new byte[bytes.length]; //随机写入
for (int i = 0; i < 100000; i++) {
mappedByteBuffer1.position(new Random(i).nextInt(bytes.length*100000));
mappedByteBuffer1.put(bytes);
}
//随机读取
for (int i = 0; i < 100000; i++) {
mappedByteBuffer2.position(new Random(i).nextInt(bytes.length*100000));
mappedByteBuffer2.get(to);
}
}
}

在这里,我们初始化了一个文件,并把它映射到了128M的内存中。分FileChannel还有MMAP的方式,通过顺序或随机读写,写了一些内容并读取一部分内容。

运行结果是:

FileChannel初始化时间:7ms
MMAPFile初始化时间:8ms
FileChannel顺序读写时间:420ms
MMAPFile顺序读写时间:20ms
FileChannel随机读写时间:860ms
MMAPFile随机读写时间:45ms

可以看到,通过MMAP内存映射文件的方式操作文件,更加快速,并且性能提升的相当明显。

微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer

2021-2-19:请问你知道 Java 如何高性能操作文件么?的更多相关文章

  1. Java中创建操作文件和文件夹的工具类

    Java中创建操作文件和文件夹的工具类 FileUtils.java import java.io.BufferedInputStream; import java.io.BufferedOutput ...

  2. File类的特点?如何创建File类对象?Java中如何操作文件内容,什么是Io流Io流如何读取和写入文件?字节缓冲流使用原则?

    重难点提示 学习目标 1.能够了解File类的特点(存在的意义,构造方法,常见方法) 2.能够了解什么是IO流以及分类(IO流的概述以及分类) 3.能够掌握字节输出流的使用(继承体系结构介绍以及常见的 ...

  3. java之高效操作文件

    代码: import java.io.IOException; import java.nio.file.FileVisitOption; import java.nio.file.FileVisit ...

  4. 日常Javaweb 2021/11/19

    Javaweb Dao层: //连接数据库,实现增查功能 package dao; import java.sql.Connection; import java.sql.DriverManager; ...

  5. 2021.12.19 eleveni的刷题记录

    2021.12.19 eleveni的刷题记录 0. 本次记录有意思的题 0.1 每个点恰好经过一次并且求最小时间 P2469 [SDOI2010]星际竞速 https://www.luogu.com ...

  6. 2021.07.19 P2294 狡猾的商人(差分约束)

    2021.07.19 P2294 狡猾的商人(差分约束) [P2294 HNOI2005]狡猾的商人 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 重点: 1.差分约束最长路与最短 ...

  7. 2021.07.19 P2624 明明的烦恼(prufer序列,为什么杨辉三角我没搞出来?)

    2021.07.19 P2624 明明的烦恼(prufer序列,为什么杨辉三角我没搞出来?) [P2624 HNOI2008]明明的烦恼 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn ...

  8. 2021.07.19 BZOJ2654 tree(生成树)

    2021.07.19 BZOJ2654 tree(生成树) tree - 黑暗爆炸 2654 - Virtual Judge (vjudge.net) 重点: 1.生成树的本质 2.二分 题意: 有一 ...

  9. 《手把手教你》系列技巧篇(五十七)-java+ selenium自动化测试-下载文件-下篇(详细教程)

    1.简介 前边几篇文章讲解完如何上传文件,既然有上传,那么就可能会有下载文件.因此宏哥就接着讲解和分享一下:自动化测试下载文件.可能有的小伙伴或者童鞋们会觉得这不是很简单吗,还用你介绍和讲解啊,不说就 ...

随机推荐

  1. Node.js 安全指南

    当项目周期快结束时,开发人员会越来越关注应用的"安全性"问题.一个安全的应用程序并不是一种奢侈,而是必要的.你应该在开发的每个阶段都考虑应用程序的安全性,例如系统架构.设计.编码, ...

  2. 三:Spring Security 登录添加验证码

    Spring Security 登录添加验证码 1.准备验证码 2.自定义过滤器 3.配置 1.准备验证码 要有验证码,首先得先准备好验证码,本文采用 Java 自画的验证码,代码如下: /** * ...

  3. 【xml】控件常用属性

    https://www.cnblogs.com/xqz0618/p/textview.html (常用,生动) https://www.jianshu.com/p/992324336dd9 (全,简洁 ...

  4. Linux lsblk和df命令区别

    lsblk 查看的是block device,也就是逻辑磁盘大小. df查看的是file system, 也就是文件系统层的磁盘大小

  5. Web程序设计基础期末大作业——模仿QQ飞车手游S联赛官网编写的网页

    QQ飞车手游是我非常喜欢的游戏,也是我现在为数不多的常在玩的游戏,刚好我Web程序设计基础的大作业是要做一套网站,我就借此机会模仿飞车S联赛官网的页面自己做了一个网页,又加了一些自己的元素,由于我做这 ...

  6. CF600 div2 F.Cheap Robot(思维+最短路+最小瓶颈路)

    最开始啃这题的时候我还是个不会$lca$的人,看代码看的没有一点头绪,现在趁着寒假补了很多关于图论的知识点,回头在看这题还是有很多值得学习的地方. Solution 1 (offline): 原题解: ...

  7. SOS DP学习笔记

    Sum over Subsets(SOS) DP 一.引入 给出一个长度为\(2^n\)的数组\(A\),对于每一个\(mask< 2^n\)要求计算出\(f[mask]=\sum_{sub\i ...

  8. Codeforces Round #547 (Div. 3) E. Superhero Battle (数学)

    题意:有一个HP为\(h\)的大怪兽,你需要轮流进行\(i\)次操作.每次可以使\(h+=d_i\)(\(d_i\)有正有负),当第\(n\)次操作完成后,再从第一次开始,问能否使得怪兽的HP变为\( ...

  9. CF1462-C. Unique Number

    题意: 给出一个数字x,让你找出一个由1到9这九个数字组成的数字,这个数字的每一位加起来等于x,并且1到9每个数字只能出现一次.若能找到这样的数字,输出这其中最小的一个,否则输出-1. 思路: 利用二 ...

  10. [Python] Pandas 对数据进行查找、替换、筛选、排序、重复值和缺失值处理

    目录 1. 数据文件 2. 读数据 3. 查找数据 4. 替换数据 4.1 一对一替换 4.2 多对一替换 4.3 多对多替换 5. 插入数据 6. 删除数据 6.1 删除列 6.2 删除行 7. 处 ...