内存映射文件(Memory-Mapped File)
Java Memory-Mapped File所使用的内存分配在物理内存而不是JVM堆内存,且分配在OS内核。
1:
内存映射文件及其应用 - 实现一个简单的消息队列 / 计算机程序的思维逻辑
在一般的文件读写中,会有两次数据拷贝,一次是从硬盘拷贝到操作系统内核,另一次是从操作系统内核拷贝到用户态的应用程序。而在内存映射文件中,一般情况下,只有一次拷贝,且内存分配在操作系统内核,应用程序访问的就是操作系统的内核内存空间,这显然要比普通的读写效率更高。
内存映射文件的另一个重要特点是,它可以被多个不同的应用程序共享,多个程序可以映射同一个文件,映射到同一块内存区域,一个程序对内存的修改,可以让其他程序也看到,这使得它特别适合用于不同应用程序之间的通信。比普通的基于loopback接口的Socket要快10倍。
简单总结下,对于一般的文件读写不需要使用内存映射文件,但如果处理的是大文件,要求极高的读写效率,比如数据库系统或繁忙的电子交易系统,或者需要在不同程序间进行共享和通信,那就可以考虑内存映射文件。
2、
为何要在Java中使用内存映射文件(Memory Mapped File)或者MappedByteBuffer
1). Java语言通过java.nio包支持内存映射文件和IO。
2). 内存映射文件用于对性能要求高的系统中,如繁忙的电子交易系统
3). 使用内存映射IO你可以将文件的一部分加载到内存中
4). 如果被请求的页面不在内存中,内存映射文件会导致页面错误
5). 将一个文件区间映射到内存中的能力取决于内存的可寻址范围。在32位机器中,不能超过4GB,即2^32比特。
6). Java中的内存映射文件比流IO要快(译注:对于大文件而言是对的,小文件则未必)
7). 用于加载文件的内存在Java的堆内存之外,存在于共享内存中,允许两个不同进程访问文件。顺便说一下,这依赖于你用的是direct还是non-direct字节缓存。
8). 读写内存映射文件是操作系统来负责的,因此,即使你的Java程序在写入内存后就挂掉了,只要操作系统工作正常,数据就会写入磁盘。
9). Direct字节缓存比non-direct字节缓存性能要好
10). 不要经常调用MappedByteBuffer.force()方法,这个方法强制操作系统将内存中的内容写入硬盘,所以如果你在每次写内存映射文件后都调用force()方法,你就不能真正从内存映射文件中获益,而是跟disk IO差不多。
11). 如果电源故障或者主机瘫痪,有可能内存映射文件还没有写入磁盘,意味着可能会丢失一些关键数据。
12). MappedByteBuffer和文件映射在缓存被GC之前都是有效的。sun.misc.Cleaner可能是清除内存映射文件的唯一选择。
3、Java内存映射文件
Java NIO的FileChannel 类提供了一个名为 map( )的方法,该方法可以在一个打开的文件和一个特殊类型的 ByteBuffer 之间建立一个虚拟内存映射,由 map( )方法返回的 MappedByteBuffer 对象的行为类似与基于内存的缓冲区,只不过该对象的数据元素存储在磁盘上的文件中。通过内存映射机制来访问一个文件会比使用常规方法读写高效得多,甚至比使用通道的效率都高。
映射方法: buffer = fileChannel.map(MapMode.READ_WRITE, 0, fileChannel.size());
- 映射模式:MapMode.READ_WRITE、MapMode.READ_ONLY、MapMode.PRIVATE
- 请求的映射模式将受被调用
map( )方法的 FileChannel 对象的访问权限所限制。如:若通道以只读的权限打开的却请求 MapMode.READ_WRITE
模式,则map( )方法会抛出一个 NonWritableChannelException 异常 - MapMode.PRIVATE模式表示一个写时拷贝(
copy-on-write)的映射,这意味着通过 put(
)方法所做的任何修改都会导致产生一个私有的数据拷贝并且该拷贝中的数据只有MappedByteBuffer
实例可以看到。该过程不会对底层文件做任何修改,而且一旦缓冲区被施以垃圾收集动作( garbage collected),那些修改都会丢失。
通过内存映射文件简单实现持久化消息队列:
public static void main(String[] args) throws IOException, InterruptedException {
// TODO Auto-generated method stub
BasicQueue basicQueue = new BasicQueue("src/cn/edu/buaa/mmap", "mmap_queque");
if (args.length == 1 && args[0].equals("producer")) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
basicQueue.enqueue(sc.nextLine().getBytes());
}
} else {
while (true) {
byte[] data = basicQueue.dequeue();
if (null != data) {
System.out.println(new String(data));
} else {
System.out.println(data);
}
Thread.sleep(1000);
}
}
}
}
class BasicQueue {// 为简化起见,我们暂不考虑由于并发访问等引起的一致性问题。
// 队列最多消息个数,实际个数还会减1
private static final int MAX_MSG_NUM = 1024;
// 消息体最大长度
private static final int MAX_MSG_BODY_SIZE = 20;
// 每条消息占用的空间
private static final int MSG_SIZE = MAX_MSG_BODY_SIZE + 4;
// 队列消息体数据文件大小
private static final int DATA_FILE_SIZE = MAX_MSG_NUM * MSG_SIZE;
// 队列元数据文件大小 (head + tail)
private static final int META_SIZE = 8;
private MappedByteBuffer dataBuf;
private MappedByteBuffer metaBuf;
public BasicQueue(String path, String queueName) throws IOException {
if (!path.endsWith(File.separator)) {
path += File.separator;
}
System.out.println(path);
RandomAccessFile dataFile = null;
RandomAccessFile metaFile = null;
try {
dataFile = new RandomAccessFile(path + queueName + ".data", "rw");
metaFile = new RandomAccessFile(path + queueName + ".meta", "rw");
dataBuf = dataFile.getChannel().map(MapMode.READ_WRITE, 0, DATA_FILE_SIZE);
metaBuf = metaFile.getChannel().map(MapMode.READ_WRITE, 0, META_SIZE);
} finally {
if (dataFile != null) {
dataFile.close();
}
if (metaFile != null) {
metaFile.close();
}
}
}
public void enqueue(byte[] data) throws IOException {
if (data.length > MAX_MSG_BODY_SIZE) {
throw new IllegalArgumentException(
"msg size is " + data.length + ", while maximum allowed length is " + MAX_MSG_BODY_SIZE);
}
if (isFull()) {
throw new IllegalStateException("queue is full");
}
int tail = tail();
dataBuf.position(tail);
dataBuf.putInt(data.length);
dataBuf.put(data);
if (tail + MSG_SIZE >= DATA_FILE_SIZE) {
tail(0);
} else {
tail(tail + MSG_SIZE);
}
}
public byte[] dequeue() throws IOException {
if (isEmpty()) {
return null;
}
int head = head();
dataBuf.position(head);
int length = dataBuf.getInt();
byte[] data = new byte[length];
dataBuf.get(data);
if (head + MSG_SIZE >= DATA_FILE_SIZE) {
head(0);
} else {
head(head + MSG_SIZE);
}
return data;
}
private int head() {
return metaBuf.getInt(0);
}
private void head(int newHead) {
metaBuf.putInt(0, newHead);
}
private int tail() {
return metaBuf.getInt(4);
}
private void tail(int newTail) {
metaBuf.putInt(4, newTail);
}
private boolean isEmpty() {
return head() == tail();
}
private boolean isFull() {
return ((tail() + MSG_SIZE) % DATA_FILE_SIZE) == head();
}
}
内存映射文件(Memory-Mapped File)的更多相关文章
- C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转
原文:C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing ...
- C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 VC中进程与进程之间共享内存 .net环境下跨进程、高频率读写数据 使用C#开发Android应用之WebApp 分布式事务之消息补偿解决方案
C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). ...
- C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped
节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). 内存映射文件对于托管世界的开发人员来说似乎很陌生,但它确实已经是很远古的技术了,而且在操作 ...
- 第17章 内存映射文件(3)_稀疏文件(Sparse File)
17.8 稀疏调拨的内存映射文件 17.8.1 稀疏文件简介 (1)稀疏文件(Sparse File):指的是文件中出现大量的0数据,这些数据对我们用处不大,但是却一样的占用空间.NTFS文件系统对此 ...
- .NET 4.0中使用内存映射文件实现进程通讯
操作系统很早就开始使用内存映射文件(Memory Mapped File)来作为进程间的共享存储区,这是一种非常高效的进程通讯手段.Win32 API中也包含有创建内存映射文件的函数,然而,这些函数都 ...
- JAVA I/O(三)内存映射文件
<Java编程思想>中对内存映射文件有详细的介绍,此处仅做简单记录和总结.内存映射文件允许创建和修改因为太大而不能放入内存的文件. 1. 内存映射文件简单实例 import java.io ...
- 虚拟内存(VirtualAlloc),堆(HeapAlloc/malloc/new)和Memory Mapped File
http://blog.csdn.net/zj510/article/details/39400087 内存管理有三种方式: 1. 虚拟内存,VirtualAlloc之类的函数 2. 堆,Heapxx ...
- 《windows核心编程》 17章 内存映射文件
内存映射文件主要用于以下三种情况: 系统使用内存映射文件载入并运行exe和dll,这大量节省了页交换文件的空间以及应用程序的启动时间 开发人员可以使用内存映射文件来访问磁盘上的数据文件.这使得我们可以 ...
- JAVA NIO之浅谈内存映射文件原理与DirectMemory
JAVA类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...
随机推荐
- ansible 学习记录
Ansible 的重新学习记录 这里我的Ansible的宿主机是centos 7.2系统,这里我通过yum 安装Ansible 1.配置epel源 sudo yum -y install epel-r ...
- 面试题一:linux面试题
2.4 写出一种排序算法(原理),并说出优化它的方法. 2.5 请简单阐述您最得意的开发之作 2.6 对于大流量的网站,您采用什么样的方法来解决各页面访问量统计问题 a. 确认服务器是否能支撑当前访问 ...
- sqlite 判断表中是否包含 某个字段
数据库 都有一个 根表..(我的理解) 也就是 你创建了一个数据库 里面就带有 一个表 sqlite_master 字段有 type , name , tbl_name , rootpage ,sq ...
- mybatis 对象关系映射例子
入门 http://legend2011.blog.51cto.com/3018495/908956 增删改 http://legend2011.blog.51cto.com/3018495/9130 ...
- Ultraedit使用小技巧
4. 编辑文件如何加入时间戳 ?F7 快捷键即可.你试试看? 5. 为何拷贝(Copy)/粘贴(Paste)功能不能用了?不怕大家笑话,我有几次使用 UltraEdit的过程中发现拷贝与粘贴的内容是不 ...
- ES6模块的import和export用法
ES6之前已经出现了js模块加载的方案,最主要的是CommonJS和AMD规范.commonjs主要应用于服务器,实现同步加载,如nodejs.AMD规范应用于浏览器,如requirejs,为异步加载 ...
- 多个inputstream的情况下,watermark的值怎么赋值? kakfa中多个partition提取 watermark
1,org.apache.flink.streaming.api.operators; AbstractStreamOperator public void processWatermark1(Wat ...
- 使用 "java -jar"命令启动jar包时报不支持的jdk版本异常
在使用java -jar 命令启动一个jar包的时候,提示版本不支持: [root@hadoop01 eureka-server-master]# java -jar xuebusi-eureka-s ...
- 说说Python程序的执行过程
1. Python是一门解释型语言? 我初学Python时,听到的关于Python的第一句话就是,Python是一门解释性语言,我就这样一直相信下去,直到发现了*.pyc文件的存在.如果是解释型语言, ...
- 【造轮子】MFC实现BlockingQueue
最近任务需要在MFC下做多线程生产者消费者模式的东西,我找了半天貌似MFC没有类似Java里面BlockingQueue那样的工具(也许是我手残没找到). 网上好像也有很多大佬去实现这个.但是我没仔细 ...