Что делать при возникновении OutOfMemoryException при загрузке файла
Что проверяют
Знаете ли устройство ошибок нехватки памяти в JVM и лечите ли причину, а не симптом. Заодно проверяют внимательность: класса OutOfMemoryException в Java нет.
Как отвечать
Поправить название спокойно и по делу: это OutOfMemoryError, наследник Error, а не исключения. Ловить его в обработчике смысла нет, состояние процесса после него уже неизвестно.
Дальше смотреть на сообщение, они означают разное:
Java heap space: не нашлось места в куче, обычный случай при загрузке файла;GC overhead limit exceeded: память формально есть, но сборщик крутится вхолостую;Direct buffer memory: кончилась память вне кучи, лимит задаётся отдельно от-Xmx;Metaspace: переполнены метаданные классов, к файлам отношения не имеет.
Причина при загрузке почти всегда одна: файл читают целиком. Чтение всего тела в массив байт создаёт объект размером с файл, и лечь он должен одним непрерывным куском. Решение: работать потоком, читать буфером фиксированного размера или построчно, писать сразу получателю, а не копить в памяти. В Spring дополнительно ограничить максимальный размер загрузки настройками multipart и задать порог, после которого содержимое уходит во временный файл на диске.
Полезно упомянуть снятие дампа кучи при падении и его разбор: видно, какие объекты заняли память. Увеличение -Xmx уместно, только когда доказано, что объём законный.
Где спотыкаются
Ловят ошибку в try/catch и продолжают работу. Куча уже исчерпана, соседние потоки падают тем же самым, и приложение отвечает мусором вместо честного отказа.
Первым делом поднимают -Xmx. Это отодвигает падение до следующего файла побольше, а заодно приближает момент, когда нехватку памяти заметит ядро и убьёт процесс целиком.
Считают, что занятая память равна размеру файла. Разбор в объекты или строку раздувает его в разы: символ строки не равен байту, а у каждого объекта есть заголовок и ссылки.