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