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. hadoop集群搭建实践

    一.Linux配置1.设置hosts的ip(集群机器做同样操作)vi /etc/hosts192.168.91.129 master192.168.91.130 slave #互相ping通即可 pi ...

  2. iOS缓存-内存缓存

    为了减少与服务器的连接沟通次数,提高应用程序的执行速度,使用了iOS的缓存机制 #import "YoungViewController.h" @interface YoungVi ...

  3. 【Unity】6.3 通过 C# 脚本创建和访问游戏对象

    分类:Unity.C#.VS2015 创建日期:2016-04-16 一.简介 在游戏开发过程中,脚本不但需要访问脚本所在的游戏对象的组件,还经常需要访问和控制真他游戏对象.另外,根据项目需求,还可能 ...

  4. Eclipse 配置Maven以及修改默认Repository

    今天将Eclipse关于Maven的配置总结一下,方便以后配置. 一.配置Maven环境 1.下载apache-maven文件,选择自己需要的版本,地址:http://mirrors.cnnic.cn ...

  5. Quartz.Net定时任务EF+MVC版的web服务

    之前项目采用JAVA 的 Quartz 进行定时服调度务处理程序,目前在.NET下面使用依然可以完成相同的工作任务,其实什么语言不重要,关键是我们要学会利用语言实现价值.它是一个简单的执行任务计划的组 ...

  6. Mac终端中主机名与用户名的修改

    Mac终端的主机名称+用户名全部显示出来的话会可能显得特别长,影响整体美观,可以切换至root用户后 修改.bashrc文件进行格式化. 1. 终端中切换root用户 su - root 2. 修改/ ...

  7. 【Socket】Socket网络编程常用的结构及函数小结

    名词解析 IP地址的作用是标示计算机的网卡地址,每台计算机都有一个IP地址: 端口是指计算机中为了标示在计算机中访问网络的不同程序而设的编号,并不是网卡接线的端口,而是不同程序的逻辑编号,并不是实际存 ...

  8. hive一行变多行及多行变一行

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

  9. Python3之pymysql导入mysql

    $cat insert.py #!/usr/bin/python # -*- coding: UTF-8 -*- import os import sys import datetime import ...

  10. Lintcode: Majority Number 解题报告

    Majority Number 原题链接:http://lintcode.com/en/problem/majority-number/# Given an array of integers, th ...