Картирование памяти Java, легко обрабатывать большие файлы на G

Java
Картирование памяти Java, легко обрабатывать большие файлы на G

Памятные файлы(Файл с отображением памяти), который относится к отображению части виртуальной памяти в файл побайтно, так что приложение обрабатывает файл, как если бы оно обращалось к основной памяти (но оно не потребляет физическую память до того, как фактически с использованием данных, а также нет операций чтения и записи на диск), что на порядки быстрее, чем прямое чтение и запись файлов.

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

Файлы с отображением памяти в основном используются для повышения производительности ввода-вывода, особенно для больших файлов. Для небольших файлов файлы с отображением памяти приведут к пустой трате фрагментированного пространства, поскольку отображение памяти всегда выравнивается по границам страницы, а минимальная единица измерения составляет 4 КБ. 3 КБ оперативной памяти.

Пакет java.nio делает отображение памяти очень простым.Основной класс называется MappedByteBuffer, что буквально означает отображаемый байтовый буфер.

01. Используйте MappedByteBuffer для чтения файлов

Предположим теперь, что есть файл с именем cmower.txt, содержимое которого:

Silent Wang Er, интересный программист

PS: Эй, я не могу изменить вонючую проблему продажи Ван По дынь и хвастовства, потому что я боюсь, что статья будет украдена.

Этот файл находится в/resourceкаталог, мы можем получить его следующими способами:

ClassLoader classLoader = Cmower.class.getClassLoader();
Path path = Paths.get(classLoader.getResource("cmower.txt").getPath());

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

Затем из файла получить канал (канал, абстракцию для дисковых файлов).

FileChannel fileChannel = FileChannel.open(path);

Затем из CHANNEL вызывается метод MAP класса FileChannel, который расширяется.ByteBuffer- Предоставляет некоторые основные методы работы с отображаемыми в память файлами.

MappedByteBuffer mappedByteBuffer = fileChannel.map(mode, position, size);

Объясните немного о трех параметрах метода карты.

1) режим — это режим сопоставления файлов, который делится на три типа:

  • MapMode.READ_ONLY (только чтение), любая попытка изменить буфер приведет к возникновению исключения ReadOnlyBufferException.

  • MapMode.READ_WRITE (чтение/запись), любые изменения в буфере будут записаны в файл в какой-то момент. Следует отметить, что другие программы, отображающие один и тот же файл, могут не сразу увидеть эти изменения, а поведение нескольких программ, одновременно отображающих файлы, зависит от операционной системы.

  • MapMode.PRIVATE (private), изменения в буфере не записываются в файл, любые изменения являются приватными для этого буфера.

2) position — начальная позиция отображения файла.

3)size- размер области, подлежащей нанесению на карту, должен быть неотрицательным числом и не может быть больше, чемInteger.MAX_VALUE.

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

CharBuffer charBuffer = null;
ClassLoader classLoader = Cmower.class.getClassLoader();
Path path = Paths.get(classLoader.getResource("cmower.txt").getPath());
try (FileChannel fileChannel = FileChannel.open(path)) {
    MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_ONLY, 0, fileChannel.size());
    
    if (mappedByteBuffer != null) {
        charBuffer = Charset.forName("UTF-8").decode(mappedByteBuffer);
    }
    
    System.out.println(charBuffer.toString());
} catch (IOException e) {
    e.printStackTrace();
}

так какdecode()Параметр метода — MappedByteBuffer, что означает, что мы читаем содержимое файла из памяти, а не с диска, поэтому скорость будет очень высокой.

02. Используйте MappedByteBuffer для записи в файл

Предположим, теперь вы хотите записать следующее содержимое в файл с именем cmower1.txt.

Сайлент Ван Эр, автор книги «Дорога к продвинутой полнофункциональной веб-разработке»

Этот файл еще не создан, и его планируется разместить в каталоге пути к классам проекта.

 Path path = Paths.get("cmower1.txt");

Конкретное расположение показано на рисунке ниже.

Затем создайте канал для файла.

FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE,
                StandardOpenOption.TRUNCATE_EXISTING)

Метод open по-прежнему используется, но добавляются три параметра: первые два хорошо понятны, указывая на то, что файл доступен для чтения (READ) и записи (WRITE), третий параметр TRUNCATE_EXISTING означает, что если файл уже существует, а файл имеет Open будет операция WRITE, его длина усекается до 0.

Затем по-прежнему вызывайте метод map класса FileChannel, чтобы получить MappedByteBuffer из канала.

 MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_WRITE, 0, 1024);

这一次,我们把模式调整为 MapMode.READ_WRITE,并且指定文件大小为 1024,即 1KB 的大小。然后使用 MappedByteBuffer 中的 put() 方法将 CharBuffer 的内容保存到文件中。 Конкретный пример кода выглядит следующим образом.

CharBuffer charBuffer = CharBuffer.wrap("沉默王二,《Web全栈开发进阶之路》作者");

Path path = Paths.get("cmower1.txt");

try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE,
        StandardOpenOption.TRUNCATE_EXISTING)) {
    MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_WRITE, 0, 1024);

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

} catch (IOException e) {
    e.printStackTrace();
}

Открыть CMower1.txt может посмотреть на контент, подтверждающий ожидаемое содержание не записало успех.

03. Сожаление о MappedByteBuffer

Говорят, что использование MappedByteBuffer в Java — вещь очень хлопотная и болезненная, основные проявления:

1) Желательно, чтобы размер карты был ограничен примерно 1,5 ГБ. Повторяющиеся карты увеличат нагрузку на восстановление и перераспределение виртуальной памяти. То есть, если размер файла неизвестен, это не очень удобно.

2) Виртуальная память определяется операционной системой, когда она сбрасывается на диск, и это время не так легко контролируется программой.

3) Метод утилизации MappedByteBuffer странный.

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

04. Сравните время обработки файловых операций

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

Чтобы сделать вывод, я назвал еще трех участников: InputStream (обычный поток ввода), BufferedInputStream (буферизованный поток ввода), RandomAccessFile (файл произвольного доступа).

Читаемый объект - Pirates of the Caribbean 4 Stranger Tides.mkv, размер 1.71G.

1) Обычный входной поток

public static void inputStream(Path filename) {
    try (InputStream is = Files.newInputStream(filename)) {
        int c;
        while((c = is.read()) != -1) {
            
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

2) Буферизованный входной поток

public static void bufferedInputStream(Path filename) {
    try (InputStream is = new BufferedInputStream(Files.newInputStream(filename))) {
        int c;
        while((c = is.read()) != -1) {
            
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

3) файл произвольного доступа

public static void randomAccessFile(Path filename) {
    try (RandomAccessFile randomAccessFile  = new RandomAccessFile(filename.toFile(), "r")) {
        for (long i = 0; i < randomAccessFile.length(); i++) {
            randomAccessFile.seek(i);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

4) Памятный файл

public static void mappedFile(Path filename) {
    try (FileChannel fileChannel = FileChannel.open(filename)) {
        long size = fileChannel.size();
        MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_ONLY, 0, size);
        for (int i = 0; i < size; i++) {
            mappedByteBuffer.get(i);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Программа тестирования тоже очень проста, примерно так:

long start = System.currentTimeMillis();
bufferedInputStream(Paths.get("jialebi.mkv"));
long end = System.currentTimeMillis();
System.out.println(end-start);

Результаты четырех участников представлены в таблице ниже.

метод время
обычный входной поток Скорость черепахи, не терпится дождаться результата
файл с произвольным доступом Скорость черепахи, не терпится больше ждать
буферизованный входной поток 29966
файл с отображением памяти 914

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

05. Наконец

В этой статье в основном представлены отображаемые в память файлы Java, MappedByteBuffer — его душа, а скорость чтения — молниеносная. Кроме того, все эти примеры и фрагменты кода можноНайдено на GitHub- Это проект Maven, поэтому его легко импортировать и запускать.

Добро пожаловать в официальную учетную запись «Silent King II» и ответьте на ключевое слово «Java» в фоновом режиме, чтобы получить руководство по общему алгоритму Java — обязательное руководство для мастеров.

扫码关注