Using Java MappedByteBuffer – 使用JavaMappedByteBuffer

最后修改: 2017年 4月 30日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this quick article, we’ll be looking at the MappedByteBuffer in the java.nio package. This utility can be quite useful for efficient file reads.

在这篇快速文章中,我们将关注MappedByteBuffer中的java.nio包。这个工具对于高效的文件读取相当有用。

2. How MappedByteBuffer Works

2.MappedByteBuffer W是如何工作的

When we’re loading a region of the file, we can load it to the particular memory region that can be accessed later.

当我们加载文件的一个区域时,我们可以把它加载到以后可以访问的特定内存区域。

When we know that we’ll need to read the content of a file multiple times, it’s a good idea to optimize the costly process e.g. by saving that content in the memory. Thanks to that, subsequent lookups of that part of the file will go only to the main memory without the need to load the data from the disc, reducing latency substantially.

当我们知道我们需要多次读取一个文件的内容时,优化这个昂贵的过程是一个好主意,例如,将这些内容保存在内存中。这样一来,随后对该部分文件的查找将只进入主内存,而不需要从光盘上加载数据,大大减少了延迟。

One thing that we need to be careful with when using the MappedByteBuffer is when we’re working with very large files from disc – we need to make sure the file will fit in memory.

在使用MappedByteBuffer时,我们需要注意的一件事是,当我们从光盘上处理非常大的文件时–我们需要确保文件能装进内存

Otherwise, we can fill up the entire memory and, as a consequence, run into the common OutOfMemoryException. We can overcome that by loading only part of the file – based for example on usage patterns.

否则,我们可能会填满整个内存,结果就是遇到常见的OutOfMemoryException。我们可以通过只加载文件的一部分来克服这个问题–例如,基于使用模式。

3. Reading the File Using MappedByteBuffer

3.使用MappedByteBuffer读取文件

Let’s say that we have a file called fileToRead.txt with the following content:

假设我们有一个名为fileToRead.txt的文件,内容如下。

This is a content of the file

The file is located in the /resource directory so we can load it using the following function:

该文件位于/resource目录中,因此我们可以使用以下函数加载它。

Path getFileURIFromResources(String fileName) throws Exception {
    ClassLoader classLoader = getClass().getClassLoader();
    return Paths.get(classLoader.getResource(fileName).getPath());
}

To create the MappedByteBuffer from a file, firstly we need to create a FileChannel from it. Once we have our channel created, we can invoke the map() method on it passing in the MapMode, a position from which we want to read, and the size parameter that specifies how many bytes we want:

要从一个文件中创建MappedByteBuffer,首先我们需要从它创建一个FileChannel。一旦我们创建了通道,我们就可以调用map()方法,输入MapMode,一个我们想要读取的位置,以及size参数,指定我们想要多少个字节。

CharBuffer charBuffer = null;
Path pathToRead = getFileURIFromResources("fileToRead.txt");

try (FileChannel fileChannel (FileChannel) Files.newByteChannel(
  pathToRead, EnumSet.of(StandardOpenOption.READ))) {
 
    MappedByteBuffer mappedByteBuffer = fileChannel
      .map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());

    if (mappedByteBuffer != null) {
        charBuffer = Charset.forName("UTF-8").decode(mappedByteBuffer);
    }
}

Once we mapped our file into the memory mapped buffer, we can read the data from it into the CharBuffer. Important to note is that although we are reading the content of the file when we call the decode() method passing MappedByteBuffer, we read from memory, not from the disc. Therefore that read will be very fast.

一旦我们将文件映射到内存映射的缓冲区中,我们就可以将其中的数据读入CharBuffer。需要注意的是,尽管我们在调用decode()方法时通过MappedByteBuffer读取文件的内容,我们是从内存中读取的,而不是从磁盘中。因此,这种读取将是非常快的。

We can assert that content that we read from our file is the actual content of the fileToRead.txt file:

我们可以断言,我们从文件中读取的内容是fileToRead.txt文件的实际内容。

assertNotNull(charBuffer);
assertEquals(
  charBuffer.toString(), "This is a content of the file");

Every subsequent read from the mappedByteBuffer will be very fast because the content of the file is mapped in memory and reading is done without a need to lookup data from the disc.

随后从mappedByteBuffer的每一次读取都会非常快,因为文件的内容在内存中被映射,读取时不需要从磁盘中查找数据。

4. Writing to the File Using MappedByteBuffer

4.使用MappedByteBuffer向文件写入

Let’s say that we want to write some content into the file fileToWriteTo.txt using the MappedByteBuffer API. To achieve that we need to open the FileChannel and call the map() method on it, passing in the FileChannel.MapMode.READ_WRITE.

假设我们想使用MappedByteBuffer API将一些内容写入文件fileToWriteTo.txt中。为了实现这一目标,我们需要打开FileChannel,并对其调用map()方法,传入FileChannel.MapMode.READ_WRITE.文件通道.MapMode.READ_WRITE

Next, we can save the content of the CharBuffer into the file using the put() method from the MappedByteBuffer:

接下来,我们可以使用MappedByteBuffer的put()方法将CharBuffer的内容保存到文件中:

CharBuffer charBuffer = CharBuffer
  .wrap("This will be written to the file");
Path pathToWrite = getFileURIFromResources("fileToWriteTo.txt");

try (FileChannel fileChannel = (FileChannel) Files
  .newByteChannel(pathToWrite, EnumSet.of(
    StandardOpenOption.READ, 
    StandardOpenOption.WRITE, 
    StandardOpenOption.TRUNCATE_EXISTING))) {
    
    MappedByteBuffer mappedByteBuffer = fileChannel
      .map(FileChannel.MapMode.READ_WRITE, 0, charBuffer.length());
    
    if (mappedByteBuffer != null) {
        mappedByteBuffer.put(
          Charset.forName("utf-8").encode(charBuffer));
    }
}

We can assert that the actual content of the charBuffer was written to the file by reading the content of it:

我们可以断言,charBuffer的实际内容是通过读取它的内容写入文件的。

List<String> fileContent = Files.readAllLines(pathToWrite);
assertEquals(fileContent.get(0), "This will be written to the file");

5. Conclusion

5.结论

In this quick tutorial, we were looking at the MappedByteBuffer construct from the java.nio package.

在这个快速教程中,我们正在研究来自java.nio包的MappedByteBuffer结构。

This is a very efficient way to read the content of the file multiple times, as the file is mapped into memory and subsequent reads do not need to go to disc every time.

这是一个非常有效的多次读取文件内容的方法,因为文件被映射到内存中,后续的读取不需要每次都到磁盘上。

All these examples and code snippets can be found over on GitHub – this is a Maven project, so it should be easy to import and run as it is.

所有这些例子和代码片段都可以在GitHub上找到over–这是一个Maven项目,所以应该很容易导入并按原样运行。