一般高性能的涉及到存储框架,例如 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. Scala:case class

    Scala:case class 1.Scala中class.object.case class.case object区别 1.1 class 和 object 关系 1.2 case class ...

  2. JS从后台获取数据,前台动态添加tr标签中的td标签

    功能描述: 要求从后台查询该省份的所有城市,然后动态的再前台固定的tr标签中添加相应的td标签来展示城市基本信息: 文章目录 #一.前台jsp及js源码 jsp:在固定的tr标签中添加一个id,通过j ...

  3. Django(自定义过滤器和自定义标签)

    模版是一个用django模版语言标记过的python字符串.模版可以包含模版标签和变量. 模版标签是在一个模版里起作用的标记.比如,一个模版标签可以产生控制结构的内容(if或者for),可以获取数据库 ...

  4. docker(6)镜像的使用

    前言 Docker的三大核心概念:镜像.容器.仓库.初学者对镜像和容器往往分不清楚,学过面向对象的应该知道类和实例,这跟面向对象里面的概念很相似 我们可以把镜像看作类,把容器看作类实例化后的对象. d ...

  5. jackson学习之九:springboot整合(配置文件)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 系列文章汇总 jackson学习之一:基本信息 jac ...

  6. C - Door Man(欧拉回路_格式控制)

    现在你是一个豪宅的管家,因为你有个粗心的主人,所以需要你来帮忙管理,输入会告诉你现在一共有多少个房间,然后会告诉你从哪个房间出发,你的任务就是从出发的房间通过各个房间之间的通道,来把所有的门都关上,然 ...

  7. CF 1400F x-prime Substrings 题解【AC自动机+DP】

    CF 1400F.x-prime Substrings 题意: 给定一个由\('1'\)到\('9'\)组成的字符串\(s\)和一个数\(x\),定义一个串为\(x-prime\)串,当且仅当这个串上 ...

  8. Codeforces Round #343 (Div. 2) E. Famil Door and Roads (树形dp,lca)

    Famil Door's City map looks like a tree (undirected connected acyclic graph) so other people call it ...

  9. 踏上Revit二次开发之路 2 从“HelloWorld”入手

    2 从"HelloWorld"入手 在欧特克的官方网页上有个叫<My First Plug-in Training>的项目,号称可以让一个完全没有编程基础的人照着做出一 ...

  10. CentOS6下mysql的安装与配置

    CentOS是免费的.开源的.可以重新分发的开源操作系统,CentOS(Community Enterprise Operating System,中文意思是社区企业操作系统)是Linux发行版之一. ...