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)的更多相关文章

  1. C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转

    原文:C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing ...

  2. C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 VC中进程与进程之间共享内存 .net环境下跨进程、高频率读写数据 使用C#开发Android应用之WebApp 分布式事务之消息补偿解决方案

    C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). ...

  3. C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped

    节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). 内存映射文件对于托管世界的开发人员来说似乎很陌生,但它确实已经是很远古的技术了,而且在操作 ...

  4. 第17章 内存映射文件(3)_稀疏文件(Sparse File)

    17.8 稀疏调拨的内存映射文件 17.8.1 稀疏文件简介 (1)稀疏文件(Sparse File):指的是文件中出现大量的0数据,这些数据对我们用处不大,但是却一样的占用空间.NTFS文件系统对此 ...

  5. .NET 4.0中使用内存映射文件实现进程通讯

    操作系统很早就开始使用内存映射文件(Memory Mapped File)来作为进程间的共享存储区,这是一种非常高效的进程通讯手段.Win32 API中也包含有创建内存映射文件的函数,然而,这些函数都 ...

  6. JAVA I/O(三)内存映射文件

    <Java编程思想>中对内存映射文件有详细的介绍,此处仅做简单记录和总结.内存映射文件允许创建和修改因为太大而不能放入内存的文件. 1. 内存映射文件简单实例 import java.io ...

  7. 虚拟内存(VirtualAlloc),堆(HeapAlloc/malloc/new)和Memory Mapped File

    http://blog.csdn.net/zj510/article/details/39400087 内存管理有三种方式: 1. 虚拟内存,VirtualAlloc之类的函数 2. 堆,Heapxx ...

  8. 《windows核心编程》 17章 内存映射文件

    内存映射文件主要用于以下三种情况: 系统使用内存映射文件载入并运行exe和dll,这大量节省了页交换文件的空间以及应用程序的启动时间 开发人员可以使用内存映射文件来访问磁盘上的数据文件.这使得我们可以 ...

  9. JAVA NIO之浅谈内存映射文件原理与DirectMemory

    JAVA类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原 ...

随机推荐

  1. Android 7.0 出现 ”FileUriExposedException“ 和 ”解析包出现错误“ 异常的解决办法

    问题1 :android.os.FileUriExposedException: file:///storage/emulated/0/Android/data/com.xxx.xxx.xxx.rel ...

  2. CentOS 7 安装java

    我喜欢在centos中安装openjdk版本的java,无他,方便.虽然有一些不同之处,但不影响使用. 1.查询: yum search openjdk ,结果如下: java-1.6.0-openj ...

  3. Atitit codeblock c++开发环境建立attilax总结

    Atitit codeblock c++开发环境建立attilax总结 1.1. C++的重要意义 1 1.2. 项目ide的选项 1 1.3. 安装MinGW  (基于GCC的C++编译器)  50 ...

  4. tcp拥堵算法

    http://m.blog.csdn.net/article/details?id=6739189

  5. Market Guide for AIOps Platforms

    AIOps platforms enhance IT operations through greater insights by combining big data, machine learni ...

  6. 使用Windows 10专业版 进行VS2017开发 遇到 HTTP Error 400. The request hostname is invalid

    使用IIS Express 支持非localhost访问 只要使用域名或者本机IP地址都无法进行 iisexpress 调试  公网ip,还是127.0.0.1都出现上面那个错误 主要是新的系统环境 ...

  7. [AWS vs Azure] 云计算里AWS和Azure的探究(5) ——EC2和Azure VM磁盘性能分析

    云计算里AWS和Azure的探究(5) ——EC2和Azure VM磁盘性能分析 在虚拟机创建完成之后,CPU和内存的配置等等基本上是一目了然的.如果不考虑显卡性能,一台机器最重要的性能瓶颈就是硬盘. ...

  8. (转)Lua学习笔记1:Windows7下使用VS2015搭建Lua开发环境

    Lua学习笔记1:Windows7下使用VS2015搭建Lua开发环境(一)注意:工程必须添加两个宏:“配置属性”/“C或C++”/“预处理器”/“预处理器定义”,添加两个宏:_CRT_SECURE_ ...

  9. EL表达式取值中文再发送请求时会乱码

    问题描述: 在网站底部进行评论,点击提交按钮时,后台tomcat报错,通过火狐浏览器的firebug看到发送的POST请求体中,有一个title参数是乱码, 导致该字段超长违反了数据库字段的长度约束: ...

  10. Swiper.js的腾讯新闻演示

    演示效果地址:https://www.swiper.com.cn/demo/indexsample/: 代码: <!DOCTYPE html> <html> <head& ...