2021-2-19:请问你知道 Java 如何高性能操作文件么?
一般高性能的涉及到存储框架,例如 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 如何高性能操作文件么?的更多相关文章
- Java中创建操作文件和文件夹的工具类
Java中创建操作文件和文件夹的工具类 FileUtils.java import java.io.BufferedInputStream; import java.io.BufferedOutput ...
- File类的特点?如何创建File类对象?Java中如何操作文件内容,什么是Io流Io流如何读取和写入文件?字节缓冲流使用原则?
重难点提示 学习目标 1.能够了解File类的特点(存在的意义,构造方法,常见方法) 2.能够了解什么是IO流以及分类(IO流的概述以及分类) 3.能够掌握字节输出流的使用(继承体系结构介绍以及常见的 ...
- java之高效操作文件
代码: import java.io.IOException; import java.nio.file.FileVisitOption; import java.nio.file.FileVisit ...
- 日常Javaweb 2021/11/19
Javaweb Dao层: //连接数据库,实现增查功能 package dao; import java.sql.Connection; import java.sql.DriverManager; ...
- 2021.12.19 eleveni的刷题记录
2021.12.19 eleveni的刷题记录 0. 本次记录有意思的题 0.1 每个点恰好经过一次并且求最小时间 P2469 [SDOI2010]星际竞速 https://www.luogu.com ...
- 2021.07.19 P2294 狡猾的商人(差分约束)
2021.07.19 P2294 狡猾的商人(差分约束) [P2294 HNOI2005]狡猾的商人 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 重点: 1.差分约束最长路与最短 ...
- 2021.07.19 P2624 明明的烦恼(prufer序列,为什么杨辉三角我没搞出来?)
2021.07.19 P2624 明明的烦恼(prufer序列,为什么杨辉三角我没搞出来?) [P2624 HNOI2008]明明的烦恼 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn ...
- 2021.07.19 BZOJ2654 tree(生成树)
2021.07.19 BZOJ2654 tree(生成树) tree - 黑暗爆炸 2654 - Virtual Judge (vjudge.net) 重点: 1.生成树的本质 2.二分 题意: 有一 ...
- 《手把手教你》系列技巧篇(五十七)-java+ selenium自动化测试-下载文件-下篇(详细教程)
1.简介 前边几篇文章讲解完如何上传文件,既然有上传,那么就可能会有下载文件.因此宏哥就接着讲解和分享一下:自动化测试下载文件.可能有的小伙伴或者童鞋们会觉得这不是很简单吗,还用你介绍和讲解啊,不说就 ...
随机推荐
- Jmeter(三十七) - 从入门到精通进阶篇 - 输出HTML格式的性能测试报告(详解教程)
1.简介 相对于Loadrunner,Jmeter其实也是可以有测试报告产出的,虽然一般都不用(没有Loadrunner的报告那么强大是一方面),但是有小伙伴们私下问,那宏哥还是顺手写一下吧,今天我们 ...
- java的几种对象(PO,VO,DAO,BO,POJO)
一.PO persistant object 持久对象,可以看成是与数据库中的表相映射的java对象.最简单的PO就是对应数据库中某个表中的一条记录,多个记录可以用PO的集合.PO中应该不包含任何对数 ...
- 环境变量jdk版本与java -version显示不一致
问题描述: 问题产生原因: 1.Path环境变量配置了Oracle 2.C:\windows\System32下,还有java.exe. 问题描述: 今天遇到一个小bug,我电脑环境变量配置的版本是j ...
- (25)Vim 1
1.安装Vim CentOS 系统中,使用如下命令即可安装 Vim: yum install vim 需要注意的是,此命令运行时,有时需要手动确认 [y/n] 遇到此情况,选择 "y&quo ...
- 前端基础之html学习一:
网站的建站流程 页面图例 网页的结构 WEB标准 WEB标准是网页制作的标准,它不是一个标准,它是根据网页的不同组成部分生成的一系列标准.这些标准大部分由W3C起草发布,也有部分标准由ECMA起草发布 ...
- linux(1)Mac上传文件到Linux服务器
前言 我们使用mac时,想让本地文件上传至服务器,该怎么办呢 windows系统,我们可以使用xftp或者rz命令,那么mac呢? mac系统,我们可以使用sftp.scp或者rz命令,本文介绍sft ...
- 自定义 ocelot 中间件输出自定义错误信息
自定义 ocelot 中间件输出自定义错误信息 Intro ocelot 中默认的 Response 中间件在出错的时候只会设置 StatusCode 没有具体的信息,想要展示自己定义的错误信息的时候 ...
- N皇后解法以及位运算优化
N皇后解法以及位运算优化 观察棋盘,要求皇后之间不能处在同行同列同一条斜线,求使得每行都有一个皇后的放置方法共有多少种. 每尝试放置一个皇后,都可以把该位置所在的行.列标号用一个数组标记,含义表示该行 ...
- bzoj1500: [NOI2005]维修数列 (Splay+变态题)
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 11353 Solved: 3553 [Submit][Status][Discuss] Descrip ...
- hdu 4521 小明系列问题——小明序列 线段树
题意: 给你一个长度为n的序列v,你需要输出最长上升子序列,且要保证你选的两个相邻元素之间在原数组中的位置之差大于d 题解: 这个就是原来求最长上升子序列的加强版,这个思路和最长上升子序列的差不多 ...