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.简介 前边几篇文章讲解完如何上传文件,既然有上传,那么就可能会有下载文件.因此宏哥就接着讲解和分享一下:自动化测试下载文件.可能有的小伙伴或者童鞋们会觉得这不是很简单吗,还用你介绍和讲解啊,不说就 ...
随机推荐
- SparkStreaming算子操作,Output操作
SparkStreaming练习之StreamingTest,UpdateStateByKey,WindowOperator 一.SparkStreaming算子操作 1.1 foreachRDD 1 ...
- mysql修改最大连接数
root@localhost ~]# nano /etc/my.cnf编辑my.cnf在[mysqld]中加入:set-variable=max_connections=1000 更改 MySQL 在 ...
- .Net微服务实战之必须得面对的分布式问题
系列文章 .Net微服务实战之技术选型篇 .Net微服务实战之技术架构分层篇 .Net微服务实战之DevOps篇 .Net微服务实战之负载均衡(上) .Net微服务实战之CI/CD .Net微服务实战 ...
- docker(1)下载安装for mac
前言 Docker 提供轻量的虚拟化,你能够从Docker获得一个额外抽象层,你能够在单台机器上运行多个Docker微容器,而每个微容器里都有一个微服务或独立应用,例如你可以将Tomcat运行在一个D ...
- WPF 之 MultiBinding(多路 Binding)(四)
一.前言 有时候 UI 需要显示的信息由不止一个数据来源决定,这时候就需要使用 MultiBinding ,即多路 Binding. MultiBinding 与 Binding 一样均以 B ...
- js实现购物车左滑删除
使用 js 和jquery动画实现购物车左滑,请引入jquery库,不然会报错哦! 首页编写页面,注意布局,滑动分成两部分,商品图片和信息放在一个布局,删除和移入收藏放在一起. 其中js用到了 tou ...
- Hiho1422 Harmonic Matrix Counter (高斯消元)
16年北京站A题 真的难啊.. 题意: 定义和谐矩阵 就是每个元素和上下左右的xor值=0 输出一个超大数 然后最多800个询问 求字典序第k小的和谐矩阵 x y位置上的数 题解: 首先这个超大数的范 ...
- Java_web项目中在Java文件里面通过类装载器对资源文件读取
承接上一节:在eclipse完成对Java_web项目里面资源文件的读取 我们首先在src目录下创建一个资源文件db.properties 内容如下: url=127.0.0.1 name=root ...
- POJ1087 A Plug for UNIX(网络流)
在会议开始之前,你收集所有记者想要使用的设备,并尝试设置它们.你注意到有些设备使用没有插座的插头.你想知道这些设备是否来自建造这个房间时并不存在的国家.对于一些插座,有几个设备使用相应的插头.对于其他 ...
- cmd控制台Windows服务相关
1.创建服务 sc create ServerName binpath= "E:\MySql5.5\bin\mysqld.exe" 2.启动服务 sc start ServerNa ...
