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


1. 内存映射文件简单实例

import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class LargeMappedFiles {
    private static int LENGTH = 0x0000FFF;

    public static void main(String[] args) throws IOException{
        MappedByteBuffer out = new RandomAccessFile("test.dat", "rw")
          .getChannel() .map(FileChannel.MapMode.READ_WRITE,
0, LENGTH); for(int i = 0; i < LENGTH; i++) { out.put((byte)'x'); } for(int i = LENGTH/2; i < LENGTH/2 + 6; i++) { System.out.print((char)out.get(i)); } } }


  • 通过RandomAccessFile类获取FileChannel,使其具备读写功能。
  • 通过FileChannel的map方法,获取MappedByteBuffer,该方法包含三个参数,MapMode映射类型、开始位置、映射总数量,意味着可以映射大文件的较小部分。
  • MappedByteBuffer是一个特殊的直接缓冲器,对该缓冲器的修改会反映到对应文件中;另外,其继承ByteBuffer,具有ByteBuffer的所有方法。


2. 内存映射文件源码


     * Maps a region of this channel's file directly into memory.
     * <p> A region of a file may be mapped into memory in one of three modes:
     * </p>
     * <ul>
     *   <li><p> <i>Read-only:</i> Any attempt to modify the resulting buffer
     *   will cause a {@link java.nio.ReadOnlyBufferException} to be thrown.
     *   ({@link MapMode#READ_ONLY MapMode.READ_ONLY}) </p></li>
     *   <li><p> <i>Read/write:</i> Changes made to the resulting buffer will
     *   eventually be propagated to the file; they may or may not be made
     *   visible to other programs that have mapped the same file.  ({@link
     *   MapMode#READ_WRITE MapMode.READ_WRITE}) </p></li>
     *   <li><p> <i>Private:</i> Changes made to the resulting buffer will not
     *   be propagated to the file and will not be visible to other programs
     *   that have mapped the same file; instead, they will cause private
     *   copies of the modified portions of the buffer to be created.  ({@link
     *   MapMode#PRIVATE MapMode.PRIVATE}) </p></li>
     * </ul>
     * <p> For a read-only mapping, this channel must have been opened for
     * reading; for a read/write or private mapping, this channel must have
     * been opened for both reading and writing.
     * <p> The {@link MappedByteBuffer <i>mapped byte buffer</i>}
     * returned by this method will have a position of zero and a limit and
     * capacity of <tt>size</tt>; its mark will be undefined.  The buffer and
     * the mapping that it represents will remain valid until the buffer itself
     * is garbage-collected.
     * <p> A mapping, once established, is not dependent upon the file channel
     * that was used to create it.  Closing the channel, in particular, has no
     * effect upon the validity of the mapping.
     * <p> Many of the details of memory-mapped files are inherently dependent
     * upon the underlying operating system and are therefore unspecified.  The
     * behavior of this method when the requested region is not completely
     * contained within this channel's file is unspecified.  Whether changes
     * made to the content or size of the underlying file, by this program or
     * another, are propagated to the buffer is unspecified.  The rate at which
     * changes to the buffer are propagated to the file is unspecified.
     * <p> For most operating systems, mapping a file into memory is more
     * expensive than reading or writing a few tens of kilobytes of data via
     * the usual {@link #read read} and {@link #write write} methods.  From the
     * standpoint of performance it is generally only worth mapping relatively
     * large files into memory.  </p>
     * @param  mode
     *         One of the constants {@link MapMode#READ_ONLY READ_ONLY}, {@link
     *         MapMode#READ_WRITE READ_WRITE}, or {@link MapMode#PRIVATE
     *         PRIVATE} defined in the {@link MapMode} class, according to
     *         whether the file is to be mapped read-only, read/write, or
     *         privately (copy-on-write), respectively
     * @param  position
     *         The position within the file at which the mapped region
     *         is to start; must be non-negative
     * @param  size
     *         The size of the region to be mapped; must be non-negative and
     *         no greater than {@link java.lang.Integer#MAX_VALUE}
     * @return  The mapped byte buffer
     * @throws NonReadableChannelException
     *         If the <tt>mode</tt> is {@link MapMode#READ_ONLY READ_ONLY} but
     *         this channel was not opened for reading
     * @throws NonWritableChannelException
     *         If the <tt>mode</tt> is {@link MapMode#READ_WRITE READ_WRITE} or
     *         {@link MapMode#PRIVATE PRIVATE} but this channel was not opened
     *         for both reading and writing
     * @throws IllegalArgumentException
     *         If the preconditions on the parameters do not hold
     * @throws IOException
     *         If some other I/O error occurs
     * @see java.nio.channels.FileChannel.MapMode
     * @see java.nio.MappedByteBuffer
    public abstract MappedByteBuffer map(MapMode mode,
                                         long position, long size)
        throws IOException;
  • 该方法直接将通道对应文件的一部分映射到内存,并返回MappedByteBuffer
  • 有3种模式:READ_ONLY(只读)、READ_WRITE(读写)、PRIVATE(私有,用于copy-on-write)
  • MappedByteBuffer一旦建立,就与创建它的通道无关,即通道关闭时,不影响该缓冲器
  • 内存映射需要依赖于底层操作系统;另外,对大部分操作系统,内存映射要比直接读写昂贵,故一般都映射较大的文件。
  • 该方法的参数包括读写模式(由FileChannel内部类MapMode定义,如下)、开始位置position、映射大小size
     * A typesafe enumeration for file-mapping modes.
     * @since 1.4
     * @see java.nio.channels.FileChannel#map
    public static class MapMode {

         * Mode for a read-only mapping.
        public static final MapMode READ_ONLY
            = new MapMode("READ_ONLY");

         * Mode for a read/write mapping.
        public static final MapMode READ_WRITE
            = new MapMode("READ_WRITE");

         * Mode for a private (copy-on-write) mapping.
        public static final MapMode PRIVATE
            = new MapMode("PRIVATE");

        private final String name;

        private MapMode(String name) {
   = name;

         * Returns a string describing this file-mapping mode.
         * @return  A descriptive string
        public String toString() {
            return name;


3. 文件加锁



public abstract FileLock lock(long position, long size, boolean shared)
        throws IOException;

public final FileLock lock() throws IOException {
        return lock(0L, Long.MAX_VALUE, false);

public abstract FileLock tryLock(long position, long size, boolean shared)
        throws IOException;

public final FileLock tryLock() throws IOException {
        return tryLock(0L, Long.MAX_VALUE, false);

FileLock是对文件某区域进行标识的(A token representing a lock on a region of a file.),可以通过FileChannel和AsynchronousFileChannel的加锁方法创建,包含四个成员:

public abstract class FileLock implements AutoCloseable {

    private final Channel channel;
    private final long position;
    private final long size;
    private final boolean shared;




import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.channels.FileLock;

 * 对映射文件加锁
 * 映射文件:MappedByteBuffer out =, 0, LENGTH);
 * 加锁:FileLock fl = fc.lock(start, end, false);    fl.release();
 * @author bob
public class LockingMappedFIles {
    private static final int LENGTH = 0x0000FFF;//128M
    static FileChannel fc;

    public static void main(String[] args) throws IOException{
        fc = new RandomAccessFile("data.dat", "rw").getChannel();
        MappedByteBuffer out =, 0, LENGTH);
        //3.写入字符  x
        for(int i = 0; i < LENGTH; i++) {
        Thread thread1 = new LockAndModify(out, 0, LENGTH/3);
        Thread thread2 = new LockAndModify(out, LENGTH*2/3, LENGTH);

    static class LockAndModify extends Thread{
        private ByteBuffer byteBuffer;
        private int start, end;
        public LockAndModify(ByteBuffer byteBuffer, int start, int end) {
            this.start = start;
            this.end = end;
             * 1. 设置MappedByteBuffer的position和limit
             * 2. 调slice()方法,创建新ByteBuffer,映射原ByteBuffer;其position为0,limit为缓冲器容量
             *       由slice()方法创建的ByteBuffer是直接的、只可读的
             *       修改会映射到原ByteBuffer中
             * 3. 另外,limit 和 position不可颠倒顺序,否则position可能比limit大,报错
            this.byteBuffer = byteBuffer.slice();
        public void run() {
            try {
                FileLock fl = fc.lock(start, end, false);
                System.out.println("Locked: " + start + " to " + end);
                while(byteBuffer.position() < byteBuffer.limit()+1) {
                    byteBuffer.put(byteBuffer.position(), (byte)(byteBuffer.get()+1));
                System.out.println("release: " + start + " to " + end);
            } catch (Exception e) {
                // TODO: handle exception




2. 内存映射文件通过通道创建,可设置读写模式和限制映射区域;

3. 对文件某区域加锁可实现多线程或进程对共享资源文件不同区域并发修改;

4. MappedByteBuffer是一种特殊的直接缓冲器,对其修改会反映到文件中。

5. 通过内存映射的方式,性能要比I/O流好