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. LVL类及接口使用介绍(License Verification Library )

    原文:http://android.eoe.cn/topic/android_sdk LVL Classes and Interfaces-LVL类和接口 Table 1 lists all of t ...

  2. Lua中and、or的一些特殊使用方法

    Lua中的逻辑运算符:与(and).或(or)和非(not),与其它语言的逻辑运算符功能一致,这里不做赘述.仅仅说一点,全部的逻辑运算符将false和nil视为假,其它不论什么东西视为真.0也视为真. ...

  3. android源码上面开发App

    使用eclipse 打开源码:http://blog.csdn.net/androidlover1991/article/details/17011991 使用android studio 打开源码h ...

  4. nginx check_http_send type=http 查检测不到后端TOM的存活

    原因:定位到../conf/server.xml中 <Connector port="8020" protocol="org.apache.coyote.http1 ...

  5. FFmpeg(1)-创建支持FFmpeg的AS项目

    一.新建Android Studio项目 注意点: 1.在“Create Android Project”栏目时,须勾选“Include C++ support” 复选框: 2.在“Customize ...

  6. (原创)用C++11的std::async代替线程的创建

    c++11中增加了线程,使得我们可以非常方便的创建线程,它的基本用法是这样的: void f(int n); std::thread t(f, n + ); t.join(); 但是线程毕竟是属于比较 ...

  7. linux命令(50):comm命令的用法,求交集

    Linux comm命令 使用局限比较大,适用于特殊场合: Linux comm命令用于比较两个已排过序的文件. 排序:sort -u file 这项指令会一列列地比较两个已排序文件的差异,并将其结果 ...

  8. iOS-图片浏览器

    // //  ViewController.m //  19-图片浏览器 // //  Created by hongqiangli on 2017/7/31. //  Copyright © 201 ...

  9. 【MySQL】MySQL在CentOS的搭建

    安装mysql 查询yum服务器上可用的关于mysql的安装包: [root@localhost ~]# yum list | grep mysql mysql-libs.x86_64 5.1.71- ...

  10. hive一行变多行及多行变一行

    hive一行变多行及多行变一行 场景 name alias zhaoqiansun abc def ghi 处理数据时需要将上表处理成为下面的形式: name alias zhaoqiansun ab ...