При разработке регламентных заданий, выполняющих выгрузку отчетов во внешние файлы (например, в форматы Excel), программисты часто сталкиваются с вопросами оптимизации производительности. Типичный сценарий: фоновое задание в цикле формирует один и тот же отчет с разными параметрами, выгружает результат в ТабличныйДокумент и сохраняет его на диск. В этой статье мы подробно разберем, влияет ли очистка или пересоздание табличного документа между итерациями на скорость работы фонового процесса, как сервер 1С:Предприятия управляет памятью и какие лучшие практики помогут избежать зависания рабочих процессов.
Прежде чем перейти к практическим тестам и коду, проанализируем внутреннее устройство платформы 1С:Предприятие. Все фоновые и регламентные задания выполняются на стороне сервера в контексте рабочих процессов rphost. Управление памятью в платформе базируется на механизме подсчета ссылок на объекты:
ТабДок = Новый ТабличныйДокумент), операционная система выделяет под него область в адресном пространстве процесса rphost.Однако на практике освобождение памяти в rphost происходит не мгновенно. Платформа может удерживать освобожденную память в своем пуле для повторного использования, чтобы не тратить ресурсы на постоянные запросы к операционной системе. Если регламентное задание обрабатывает большие объемы данных без явного освобождения памяти, это может привести к чрезмерному раздуванию процесса rphost.
Рассмотрим ситуацию, когда в цикле выполняется формирование нескольких отчетов. Если мы используем одну и ту же переменную ТабДок и не очищаем ее явным образом, сценарий развития событий зависит от структуры вашего кода.
Вариант А: Перезапись переменной (правильный подход)
Если на каждой итерации цикла вы принудительно инициализируете переменную заново:
Для Каждого Параметр Из МассивПараметров Цикл
ТабДок = Новый ТабличныйДокумент;
// Формирование отчета и вывод в ТабДок
ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
КонецЦикла;
В этом случае при выполнении строки ТабДок = Новый ТабличныйДокумент; старая ссылка на предыдущий табличный документ заменяется новой. Предыдущий документ уничтожается. С точки зрения производительности этот вариант оптимален, так как каждый раз мы работаем с чистым объектом, а старый своевременно удаляется из памяти.
Вариант Б: Использование метода Очистить() без пересоздания
Иногда разработчики предпочитают использовать метод Очистить() для существующего объекта:
ТабДок = Новый ТабличныйДокумент;
Для Каждого Параметр Из МассивПараметров Цикл
ТабДок.Очистить();
// Формирование отчета и вывод в ТабДок
ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
КонецЦикла;
Метод Очистить() удаляет области, ячейки, рисунки и форматирование из табличного документа. Однако сам объект в памяти сохраняется. Разберем подводные камни этого подхода: если первый отчет был огромным (сотни тысяч строк со сложным оформлением), метод Очистить() освободит ячейки, но внутренние структуры данных самого объекта ТабличныйДокумент могут остаться фрагментированными. При последующих итерациях такой объект может работать медленнее, чем абсолютно новый.
Вариант В: Накопление данных (критическая ошибка)
Если разработчик забывает переинициализировать переменную и не вызывает метод очистки, а просто продолжает выводить данные в один и тот же объект:
// Ошибка: данные будут бесконечно дописываться в один документ
ТабДок = Новый ТабличныйДокумент;
Для Каждого Параметр Из МассивПараметров Цикл
// Вывод очередного отчета в ТабДок без очистки
ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
КонецЦикла;
В этом сценарии каждый последующий отчет будет дописываться в конец предыдущего. Это приведет к лавинообразному росту объема табличного документа. Память процесса rphost будет стремительно заполняться. Как только физическая память сервера закончится, операционная система начнет использовать виртуальную память (свопинг на жесткий диск), что снизит скорость работы регламентного задания в десятки и сотни раз. Более того, при превышении лимитов памяти кластера 1С, процесс rphost может быть принудительно перезапущен сервером, что приведет к аварийному завершению задания.
Выясним, как правильно и максимально быстро освобождать ресурсы при последовательной генерации файлов. Рассмотрим три основных инструмента управления жизненным циклом объекта ТабличныйДокумент:
Неопределено
Для того чтобы гарантировать моментальное уничтожение тяжелого объекта сразу после его записи на диск, рекомендуется явно обнулять переменную:
ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
ТабДок = Неопределено; // Память освобождается незамедлительно
Этот способ является наиболее надежным, так как мы принудительно разрываем связь переменной с объектом в памяти до начала следующей ресурсоемкой операции.
Хорошей практикой проектирования кода является вынос формирования каждого отдельного отчета в отдельную процедуру или функцию. Посмотрим на пример реализации:
Процедура ВыполнитьРегламентноеЗадание()
Для Каждого Параметр Из МассивПараметров Цикл
СформироватьИСохранитьОтчет(Параметр);
КонецЦикла;
КонецПроцедуры
Процедура СформироватьИСохранитьОтчет(Параметр)
ТабДок = Новый ТабличныйДокумент;
// Настройка отбора, компоновка, вывод в ТабДок...
ТабДок.Записать(ПолучитьИмяФайла(Параметр), ТипФайлаТабличногоДокумента.XLSX);
// При выходе из процедуры переменная ТабДок уничтожается автоматически
КонецПроцедуры
Поскольку переменная ТабДок является локальной для процедуры СформироватьИСохранитьОтчет, при выходе из нее счетчик ссылок гарантированно уменьшается до нуля, и память очищается платформой без необходимости ручного вмешательства разработчика.
Скорость работы регламентного задания также напрямую зависит от формата, в который выгружается документ. Платформа выполняет сложную конвертацию внутреннего представления документа (MXL) в структуру Excel:
Подведем итоги нашего анализа. Влияет ли очистка табличного документа на скорость выполнения фонового процесса? Да, влияет самым прямым образом. Чтобы регламентные задания не приводили к утечкам памяти на сервере и выполнялись с максимальной скоростью, следуйте простым правилам:
ТабличныйДокумент для вывода разных отчетов без предварительной очистки или пересоздания.Очистить() отдавайте предпочтение созданию нового объекта через конструктор Новый ТабличныйДокумент или присвоению переменной значения Неопределено.ТипФайлаТабличногоДокумента.XLSX вместо устаревшего XLS.