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 в таких сценариях?


Ответы (0 шт):