Ресурс в цикле выгрузки отчёта закрывается не всегда

На ревью часто встречается такой код для построчной выгрузки большого отчёта:

for (Order order : orders) { FileWriter writer = new FileWriter(file, true); writer.write(order.toCsvLine()); writer.close(); }

Выглядит рабочим на тесте из трёх записей. На реальном отчёте в десятки тысяч строк хотя бы одна запись рано или поздно бросит исключение при сериализации — например, null в обязательном поле.

Если исключение вылетело между new FileWriter и writer.close(), дескриptor не закрывается. Цикл прерывается, метод выбрасывает исключение наверх — а файловый дескриптор остаётся открытым до сборки мусора или до завершения процесса. На проде под нагрузкой это не единичный случай, а исчерпание лимита открытых файлов у всего сервиса, причём не сразу, а спустя часы работы.

Правильная версия открывает writer один раз до цикла и гарантирует закрытие через try-with-resources:

try (FileWriter writer = new FileWriter(file, true)) { for (Order order : orders) { writer.write(order.toCsvLine()); } }

Закрытие происходит в finally-блоке, который генерирует try-with-resources, даже если исключение вылетело на середине цикла.

Следующий вопрос обычно про то, что будет, если исключение бросит сам close() у writer — и как в этом случае сработает подавленное исключение (suppressed exception) при одновременном исключении из тела try. Это тот случай, когда try-with-resources ведёт себя иначе, чем ручной finally с close() без проверки.

Утечка дескрипторов — тот баг, который не ловится юнит-тестом на трёх записях и всплывает только на реальном объёме данных.

Тренажёр: 900+ вопросов и задач, мок с таймером, план повторов

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки