内存映射文件(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)相关方面的知识介绍一下原 ...
随机推荐
- html5自定义数字键盘
原理:使用div模拟输入框,避免手机原生键盘弹出,键盘使用div模拟,点击事件使用js控制,光标闪烁使用css模拟,具体代码如下: <!doctype html> <html lan ...
- 从头开始学习vue-router
一.前言 要学习vue-router就要先知道这里的路由是什么?为什么我们不能像原来一样直接用标签编写链接哪?vue-router如何使用?常见路由操作有哪些?等等这些问题,就是本篇要探讨的主要问题. ...
- unzip:unzip解压文件到指定目录
1.把文件解压到当前目录下 unzip test.zip 2.如果要把文件解压到指定的目录下,需要用到-d参数. unzip -d /temp test.zip 3.解压的时候,有时候不想覆盖已经存在 ...
- 每日英语:Google Scraps Plan to Build Hong Kong Data Center
Internet giant Google Inc. has scrapped a plan to build its own data center in Hong Kong and will in ...
- lua面向对象编程之点号与冒号的差异详细比较
首先,先来一段在lua创建一个类与对象的代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Class = {} Class.__index = Cl ...
- 【嵌入式】FS2410移植U-Boot-1.1.6
移植环境 硬件平台:FS2410 U-Boot版本:u-boot-1.1.6 编译器:arm-linux-gcc version 3.4.1 U-Boot主要目录结构 U-Boot的 stage ...
- Docker 入门(Mac环境)- part 5 stacks
part-5 stacks 简介 stack就是栈,栈的结构是什么样的呢?一层一层是紧挨着的,然后互相依赖,不能说中间少了一个.这样说就很明白了,栈实际上在docker中就相当于多个互相依赖的组件,共 ...
- How to disable SSL certificate checking with Spring RestTemplate?(使用resttemplate访问https时禁用证书检查)
How to disable SSL certificate checking with Spring RestTemplate?(使用resttemplate访问https时禁用证书检查) **** ...
- 如何使cmd窗口正确显示utf-8编码的文字
http://blog.csdn.net/ehcoing/article/details/51865922 ********************************************** ...
- 如何在Python之Flask中使用https链接
[Flask]在Flask中使用HTTPS 转自:http://www.jianshu.com/p/5ea147e03255