Java.nio.file.Files.exists() использует много памяти

Я профилирую свое приложение, и java.nio.file.Files появляется в качестве верхней точки использования памяти (профилируется с помощью JProfile).

Для меня это не имеет смысла, поскольку приложение выполняет множество преобразований XSLT и сохраняет результаты в памяти. Хотя он и считается главным претендентом на использование памяти, я бы не ожидал, что его превзойдет вызов java.nio.file.Files.exists().

Это код, о котором идет речь, который вызывает появление горячей точки памяти:

public static FileStatus getFileStatus(Path path) {
  try {
    var exists = Files.exists(path);
    var isDirectory = exists && Files.isDirectory(path);
    var lastModified = exists ? Files.getLastModifiedTime(path).toMillis() : 0;
    var size = exists ? Files.size(path) : 0;

    return new FileStatus(exists, isDirectory, lastModified, size);
  } catch (IOException e) {
    throw new RuntimeException(e);
  }
}

Почему Files.exists() занимает так много памяти и есть ли способ исправить использование памяти? Он работает на AdoptOpenJDK 11 и работает в Windows 10.

Выделение 1675 выглядит не так уж и много, соответствует ли это примерно количеству файлов, которые вы ожидаете проверить? Также будьте осторожны: иногда инструментирование памяти может влиять на оптимизацию, а горячие точки, которые вы видите, — это просто те, которые в основном оптимизированы (из-за escape-анализа и подобных инструментов) при работе без инструментов. Иногда менее мощные методы, такие как снимки памяти, могут предоставить менее детальную, но более полезную картину.

Joachim Sauer 19.04.2024 14:20
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
1
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я не мог объяснить, почему один вызов Files.exists оказывает большое влияние на память, но это доступ к файловой системе, поэтому ожидайте, что он будет вести себя так же, как при каждом доступе к файлу.

Однако все те вызовы, которые вы используете, используют Files.readAttributes внутри, поэтому вы можете сократить в 3 раза доступ к файловой системе (и выделение для нее ресурсов), изменив getFileStatus, чтобы сделать один readAttributes вызов:

public static FileStatus getFileStatus(Path path) {
    try {
        BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
        
        return new FileStatus(true, 
                              attrs.isDirectory(), 
                              attrs.lastModifiedTime().toMillis(), 
                              attrs.isDirectory() ? 0L : attrs.size());
    } catch (IOException e) {
        // Does not exist:
        return new FileStatus(false, false, 0L, 0L);
    }
}

Примечание. В Windows (и WSL) Files.size в каталоге может возвращать ненулевое значение. Это не размер содержимого. Если вы хотите сохранить такое поведение, используйте attrs.size() в качестве последнего параметра для new FileStatus().

Я отмечаю этот ответ как правильный, поскольку он устранил первоначальную проблему. Однако теперь точка доступа появляется при вызове Files.newInputStream(path). Он ведет себя так, как будто в фоновом режиме работает какой-то механизм кэширования для этих путей файловой системы. например он выделял кучу памяти для вызова Files.exists, из-за чего вызов Files.newInputStream() повторно использовал ту же память, которая была выделена для вызова Files.exists. Однако теперь, когда вызов Files.exists больше не вызывается, теперь память выделяется для вызова FIles.newInputStream().

user1428945 19.04.2024 16:03

Другие вопросы по теме