Java MappedByteBuffer

Узнайте о файлах Java, отображенных в память, и научитесь читать и записывать содержимое из файла, отображенного в память, с помощью RandomAccessFile и MemoryMappedBuffer.

1. Java-память-отображаемый ввод-вывод

Если вы знаете , как работает java IO на более низком уровне, то вы будете знать об обработке буферов, подкачке памяти и других подобных концепциях. Для обычного файлового ввода-вывода, в котором пользовательские процессы выдают системные вызовы read() и write() для передачи данных, почти всегда есть одна или несколько операций копирования для перемещения данных между этими страницами файловой системы в пространстве ядра и областью памяти в пространстве пользователя. Это происходит потому, что обычно нет выравнивания один к одному между страницами файловой системы и пользовательскими буферами.

Однако существует специальный тип операции ввода-вывода, поддерживаемый большинством операционных систем, который позволяет пользовательским процессам максимально использовать преимущества странично-ориентированной природы системного ввода-вывода и полностью избегать копирования буфера. Это называется вводом-выводом с отображением в память, и мы собираемся узнать здесь несколько вещей о файлах с отображением в память.

2. Файлы Java, отображенные в память

Отображенный в памяти ввод-вывод использует файловую систему для установления отображения виртуальной памяти из пользовательского пространства непосредственно на соответствующие страницы файловой системы. С отображенным в памяти файлом мы можем притвориться, что весь файл находится в памяти и что мы можем получить к нему доступ, просто рассматривая его как очень большой массив. Такой подход значительно упрощает код, который мы пишем для изменения файла.

Подробнее: Работа с буферами

Чтобы выполнять как запись, так и чтение в отображенных в память файлах, мы начинаем с RandomAccessFile, получаем канал для этого файла. Отображенные в память буферы байтов создаются с помощью метода FileChannel.map(). Этот класс расширяет класс ByteBuffer операциями, которые специфичны для отображенных в память областей файлов.

Отображенный буфер байтов и отображение файла, которое он представляет, остаются действительными до тех пор, пока сам буфер не будет очищен сборщиком мусора. Обратите внимание, что вы должны указать начальную точку и длину региона, который вы хотите отобразить в файле; это означает, что у вас есть возможность отображать меньшие регионы большого файла.

Пример 1: Запись в файл, отображенный в памяти

import java.io.RandomAccessFile;import java.nio.MappedByteBuffer;import java.nio.channels.FileChannel;public class MemoryMappedFileExample{static int length = 0x8FFFFFF;public static void main(String[] args) throws Exception{try(RandomAccessFile file = new RandomAccessFile("howtodoinjava.dat", "rw")){MappedByteBuffer out = file.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, length);for(int i = 0; i < length; i++){out.put((byte) 'x');}System.out.println("Finished writing");}}}

Файл, созданный с помощью указанной выше программы, имеет длину 128 МБ, что, вероятно, больше, чем позволяет ваша ОС. Файл кажется доступным сразу, потому что только его части переносятся в память, а другие части выгружаются. Таким образом, очень большой файл(до 2 ГБ) можно легко изменить.

3. Режимы сопоставления файлов

Как и обычные файловые дескрипторы, файловые сопоставления могут быть доступны для записи или только для чтения.

  • Первые два режима отображения, MapMode.READ_ONLY и MapMode.READ_WRITE, довольно очевидны. Они указывают, хотите ли вы, чтобы отображение было доступно только для чтения или чтобы разрешалось изменение отображенного файла.
  • Третий режим, MapMode.PRIVATE, указывает, что вы хотите отображение копирования при записи. Это означает, что любые изменения, которые вы вносите через put(), приведут к созданию частной копии данных, которую может видеть только экземпляр MappedByteBuffer.

    В базовый файл не будут внесены никакие изменения, а все внесенные изменения будут утеряны при сборке мусора буфера. Несмотря на то, что сопоставление копирования при записи предотвращает любые изменения в базовом файле, вы должны открыть файл для чтения/записи, чтобы настроить сопоставление MapMode.PRIVATE. Это необходимо для того, чтобы возвращаемый объект MappedByteBuffer разрешал put().

Вы заметите, что нет метода unmap(). После установки сопоставление остается в силе до тех пор, пока объект MappedByteBuffer не будет удален сборщиком мусора.

Также сопоставленные буферы не привязаны к каналу, который их создал. Закрытие связанного FileChannel не уничтожает сопоставление; только удаление самого буферного объекта нарушает сопоставление.

MemoryMappedBuffer имеет фиксированный размер, но файл, к которому он привязан, является эластичным. В частности, если размер файла изменяется во время действия отображения, часть или весь буфер может стать недоступным, могут быть возвращены неопределенные данные или могут быть выброшены непроверенные исключения.

Будьте осторожны с тем, как файлы обрабатываются другими потоками или внешними процессами, когда они отображаются в памяти.

4. Преимущества файлов, отображенных в памяти

Отображение ввода-вывода в памяти имеет ряд преимуществ по сравнению с обычным вводом-выводом:

  1. Пользовательский процесс рассматривает данные файла как память, поэтому нет необходимости выполнять системные вызовы read() или write().
  2. Когда пользовательский процесс касается отображенного пространства памяти, автоматически генерируются ошибки страниц для загрузки данных файла с диска. Если пользователь изменяет отображенное пространство памяти, затронутая страница автоматически помечается как грязная и впоследствии будет сброшена на диск для обновления файла.
  3. Подсистема виртуальной памяти операционной системы будет выполнять интеллектуальное кэширование страниц, автоматически управляя памятью в соответствии с нагрузкой на систему.
  4. Данные всегда выравниваются по странице, и копирование буфера не требуется.
  5. Очень большие файлы можно отображать без использования большого объема памяти для копирования данных.

5. Как прочитать файл, отображенный в памяти

Чтобы прочитать файл с помощью отображенного в память ввода-вывода, используйте следующий шаблон кода:

import java.io.File;import java.io.RandomAccessFile;import java.nio.MappedByteBuffer;import java.nio.channels.FileChannel;public class MemoryMappedFileReadExample{private static String bigExcelFile = "bigFile.xls";public static void main(String[] args) throws Exception{try(RandomAccessFile file = new RandomAccessFile(new File(bigExcelFile), "r")){//Get file channel in read-only modeFileChannel fileChannel = file.getChannel();//Get direct byte buffer access using channel.map() operationMappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());// the buffer now reads the file as if it were loaded in memory.System.out.println(buffer.isLoaded()); //prints falseSystem.out.println(buffer.capacity()); //Get the size based on content size of file//You can read the file from this buffer the way you like.for(int i = 0; i < buffer.limit(); i++){System.out.print((char) buffer.get()); //Print the content of file}}}}

6. Как записать в файл, отображенный в памяти

Чтобы записать данные в файл с помощью отображенного в память ввода-вывода, используйте следующий шаблон кода:

import java.io.File;import java.io.RandomAccessFile;import java.nio.MappedByteBuffer;import java.nio.channels.FileChannel;public class MemoryMappedFileWriteExample {private static String bigTextFile = "test.txt";public static void main(String[] args) throws Exception{// Create file objectFile file = new File(bigTextFile);//Delete the file; we will create a new filefile.delete();try(RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw")){// Get file channel in read-write modeFileChannel fileChannel = randomAccessFile.getChannel();// Get direct byte buffer access using channel.map() operationMappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, 4096 * 8 * 8);//Write the content using put methodsbuffer.put("howtodoinjava.com".getBytes());}}}

Оставляйте свои комментарии и мысли в разделе комментариев.

Прокрутить вверх