Javaを使うMappedByteBuffer

JavaMappedByteBufferの使用

1. 概要

この簡単な記事では、java.nioパッケージのMappedByteBufferについて説明します。 このユーティリティは、効率的なファイル読み取りに非常に役立ちます。

2. MappedByteBuffer Wがどのようにソークするか

ファイルの領域をロードするときに、後でアクセスできる特定のメモリ領域にファイルをロードできます。

ファイルのコンテンツを複数回読み取る必要があることがわかっている場合は、コストのかかるプロセスを最適化することをお勧めします。 そのコンテンツをメモリに保存します。 そのおかげで、ファイルのその部分の以降のルックアップは、ディスクからデータをロードする必要なしにメインメモリにのみ移動し、レイテンシを大幅に削減します。

MappedByteBufferを使用するときに注意する必要があるのは、ディスクからの非常に大きなファイルであるwe need to make sure the file will fit in memoryを操作する場合です。

そうしないと、メモリ全体がいっぱいになり、その結果、共通のOutOfMemoryException.に遭遇する可能性があります。たとえば、使用パターンに基づいて、ファイルの一部のみをロードすることで、これを克服できます。

3. MappedByteBufferを使用したファイルの読み取り

次の内容のfileToRead.txtというファイルがあるとします。

This is a content of the file

このファイルは/resourceディレクトリにあるため、次の関数を使用してロードできます。

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

ファイルからMappedByteBufferを作成するには、まずファイルからFileChannelを作成する必要があります。 チャネルを作成したら、そのチャネルでmap()メソッドを呼び出して、読み取り元のMapMode,apositionと、その方法を指定する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);
    }
}

ファイルをメモリマップバッファにマップすると、そこからデータをCharBuffer.に読み取ることができます。decode()メソッドを呼び出すとファイルの内容を読み取っていますが、 MappedByteBuffer,は、ディスクからではなく、メモリから読み取ります。 したがって、その読み取りは非常に高速になります。

ファイルから読み取ったコンテンツは、fileToRead.txtファイルの実際のコンテンツであると断言できます。

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

ファイルのコンテンツがメモリにマップされ、ディスクからデータを検索する必要なしに読み取りが行われるため、mappedByteBufferからの後続のすべての読み取りは非常に高速になります。

4. MappedByteBufferを使用したファイルへの書き込み

MappedByteBuffer APIを使用してファイルfileToWriteTo.txtにコンテンツを書き込みたいとしましょう。 これを実現するには、FileChannelを開き、その上でmap()メソッドを呼び出して、FileChannel.MapMode.READ_WRITE.を渡す必要があります。

次に、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));
    }
}

charBufferの実際の内容は、ファイルの内容を読み取ることでファイルに書き込まれたと断言できます。

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

5. 結論

このクイックチュートリアルでは、java.nioパッケージからのMappedByteBufferコンストラクトを見ていました。

これは、ファイルがメモリにマップされ、以降の読み取りが毎回ディスクに移動する必要がないため、ファイルの内容を複数回読み取る非常に効率的な方法です。

これらの例とコードスニペットはすべてover on GitHubにあります。これはMavenプロジェクトであるため、そのままインポートして実行するのは簡単です。