Spring Boot: Direct Memory не освобождается при загрузке файлов
Я столкнулся с проблемой управления памятью в Spring Boot приложении, и хотел бы получить совет по правильной очистке Direct Memory. Конфигурация:
• JDK: OpenJDK Runtime Environment (build 21.0.8+9-Ubuntu-0ubuntu122.04.1), OpenJDK 64-Bit Server VM
• Spring Boot: Версии 3.2.6 - 3.4.6
Код: У меня есть следующий контроллер и сервис:
@RequiredArgsConstructor
@RestController
public class MyEntityController {
private final MyEntityService service;
@PostMapping("/file")
public ResponseEntity<String> addedFileInVersion(MultipartHttpServletRequest request) throws IOException {
return service.addFileVersion(request);
}
}
@Service
@Slf4j
@RequiredArgsConstructor
public class MyEntityService {
@Transactional
public ResponseEntity<String> addFileVersion(MultipartHttpServletRequest request) throws IOException {
try {
log.info("Before reading : {} bytes", getDirectMemoryUsed());
MultipartFile file = request.getFile("file");
if (file == null) {
return ResponseEntity.badRequest().body("Файл не был передан или файл пустой.");
}
byte[] array = file.getBytes();
String fileName = file.getOriginalFilename();
storeFile(array, fileName);
log.info("After reading : {} bytes", getDirectMemoryUsed());
return ResponseEntity.ok("ok");
} catch (IOException e) {
log.error("Error processing file", e);
return ResponseEntity.badRequest().body("Ошибка при загрузке файла: " + e.getMessage());
} finally {
log.info("Finally reading : {} bytes", getDirectMemoryUsed());
}
}
private void storeFile(byte[] array, String path) throws IOException {
String decode = UriUtils.decode(path, StandardCharsets.UTF_8.toString());
synchronized (this) {
Path savedPath = Paths.get("/storage/tmp", decode);
if (Files.notExists(savedPath.getParent())) {
Files.createDirectories(savedPath.getParent());
}
Files.write(savedPath, array);
}
}
private double getDirectMemoryUsed() {
List<BufferPoolMXBean> bufferPoolMXBeans = ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class);
for (BufferPoolMXBean pool : bufferPoolMXBeans) {
if ("direct".equals(pool.getName())) {
return pool.getMemoryUsed();
}
}
return 0;
}
}
Проблема: При отправке файлов я наблюдаю увеличение использования Direct Memory, которое не уменьшается после обработки файла. Логи показывают следующее:
Before reading : 24576.0 bytes
After reading : 9.58496462E8 bytes
Finally reading : 9.58496462E8 bytes
Before reading : 9.58512846E8 bytes
After reading : 1.916984732E9 bytes
Finally reading : 1.916984732E9 bytes
Видно, что после обработки файла Direct Memory не освобождается.
Вопрос:
Я понимаю, что использование getBytes() (как и readAllBytes()) не является оптимальным решением для работы с файлами, так как загружает весь файл в память. Также я знаю, что Direct Memory находится вне кучи JVM и не управляется сборщиком мусора напрямую, а использование Cleaner не рекомендуется в большинстве случаев.
В связи с этим, у меня возникает вопрос: какие существуют альтернативные и рекомендуемые подходы для обработки файлов в Spring Boot, чтобы эффективно управлять Direct Memory и избежать проблем с OutOfMemoryError?
Какие стратегии можно использовать вместо загрузки всего файла в память, и как правильно освобождать Direct Memory в таких сценариях?