Ресурс в цикле выгрузки отчёта закрывается не всегда
На ревью часто встречается такой код для построчной выгрузки большого отчёта:
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 — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки