Как устранить ошибку «Недостаточно памяти» при массовой отмене проведения и удалении документов в 1С?

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При свертке информационных баз или регламентной очистке исторических данных разработчики часто сталкиваются с ситуацией, когда специализированная обработка аварийно завершает работу с системной ошибкой «Недостаточно памяти». Особенно часто эта проблема проявляется на конфигурациях предыдущих поколений, таких как «1С:Управление торговлей 10.3», работающих в режиме обычного приложения под управлением платформы 1С:Предприятие 8.3.

Проанализируем механику возникновения этой ошибки, разберем причины переполнения оперативной памяти и рассмотрим эффективные программные и инфраструктурные способы обхода проблемы.

Почему возникает переполнение памяти при массовой обработке данных

Выясним причину, по которой непрерывный цикл по документам приводит к аварийному падению сеанса. Проблема складывается из нескольких факторов платформы:

  1. Ограничения разрядности процесса. Если обработка запускается в 32-разрядном клиенте или на 32-разрядном рабочем процессе сервера, максимальный объем адресуемого адресного пространства ограничен системным лимитом операционной системы в 2 ГБ (в исключительных случаях до 3–4 ГБ). В условиях длительной работы память неизбежно фрагментируется, и при очередной попытке выделить непрерывный блок памяти платформа завершает работу аварийно.
  2. Удержание объектов сборщиком мусора. Если вся логика обработки выполняется в одной длинной процедуре, объекты ДокументОбъект, выборки, движения и кэши контекстов удерживаются в области видимости процедуры и не освобождаются оперативно.
  3. Каскадные обработчики и подписки на события. Стандартный вызов отмены проведения запускает программные модули документа и наборов записей, а также подписки на события. В сложных доработанных конфигурациях это порождает чтение огромного количества связанных данных, пересчет итогов и создание дополнительных транзакций.
  4. Накопление буферов СУБД. Выполнение тысяч операций модификации данных в непрерывном потоке без регулярной фиксации транзакций переполняет внутренние буферы платформы и СУБД.

Решение 1. Оптимизация платформенных настроек и разрядности

Разберем базовые системные меры, которые позволяют снизить остроту проблемы без изменения бизнес-логики конфигурации:

  1. Переход на 64-разрядную платформу. Использование 64-битного клиента и 64-битного сервера приложений 1С полностью снимает ограничение адресного пространства в 2 ГБ на процесс, позволяя использовать весь доступный объем оперативной памяти сервера.
  2. Управление итогами регистров накопления и бухгалтерии. При массовой отмене проведения документов огромная нагрузка ложится на пересчет итогов. Для ускорения процесса и экономии памяти рекомендуется временно отключить использование итогов по регистрам через метод УстановитьИспользованиеИтогов(Ложь) у соответствующих менеджеров регистров, а после завершения очистки пересчитать итоги заново.

Решение 2. Отключение прикладных обработчиков через свойство «ОбменДанными.Загрузка»

Когда требуется просто пометить объекты на удаление или отменить их проведение в рамках свертки, выполнение тяжелых алгоритмов проведения (контроль остатков, перезапись связанных регистров, проверки заполнения) является избыточным. Для отключения проверок используем специальное свойство объекта ОбменДанными.Загрузка.

Посмотрим на пример правильной пометки на удаление:


ДокОбъект = ВыбраннаяСсылка.ПолучитьОбъект();
Если ДокОбъект <> Неопределено Тогда
    ДокОбъект.ОбменДанными.Загрузка = Истина;
    ДокОбъект.УстановитьПометкуУдаления(Истина);
КонецЕсли;

Флаг ОбменДанными.Загрузка = Истина сообщает платформе и конфигурации, что данные загружаются в служебном режиме. В этом случае платформенные обработчики ПередЗаписью, ПриЗаписи и подписки на события либо пропускаются полностью, либо выполняют минимальные проверки, не создавая утечек памяти.

Решение 3. Прямая очистка движений без загрузки объектов

Отмена проведения через метод Записать(РежимЗаписиДокумента.ОтменаПроведения) требует считывания в память всего документа целиком и вызова его модуля. Если требуется очистить движения, гораздо эффективнее перезаписывать наборы записей регистров пустыми значениями с отбором по регистратору.

Рассмотрим пример прямой очистки движений документа по регистру накопления:


Набор = РегистрыНакопления.Продажи.СоздатьНаборЗаписей();
Набор.Отбор.Регистратор.Установить(СсылкаНаДокумент);
Набор.ОбменДанными.Загрузка = Истина;
// Записываем пустой набор записей, удаляя старые движения
Набор.Записать();

При таком подходе сам документ не считывается в оперативную память, что радикально уменьшает расход ресурсов процесса.

Решение 4. Разрыв области видимости и порционная фиксация транзакций

Чтобы внутренний сборщик мусора платформы мог своевременно освобождать память, необходимо избегать выполнения всей логики внутри одной большой процедуры. Вынесем обработку единичного объекта или небольшой пачки в отдельную процедуру и ограничим объем явных транзакций.

Разберем по шагам реализацию порционной обработки:


Процедура ОбработатьПачкуДокументов(МассивСсылок)
    
    НачатьТранзакцию();
    Попытка
        Для Каждого Ссылка Из МассивСсылок Цикл
            ОбработатьОдинДокумент(Ссылка);
        КонецЦикла;
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ВызватьИсключение;
    КонецПопытки;
    
КонецПроцедуры

Процедура ОбработатьОдинДокумент(Ссылка)
    
    ДокОбъект = Ссылка.ПолучитьОбъект();
    Если ДокОбъект <> Неопределено Тогда
        ДокОбъект.ОбменДанными.Загрузка = Истина;
        ДокОбъект.УстановитьПометкуУдаления(Истина);
        // Принудительно обнуляем ссылку на объект перед выходом из процедуры
        ДокОбъект = Неопределено;
    КонецЕсли;
    
КонецПроцедуры

Поясним особенности этого механизма: при выходе из процедуры ОбработатьОдинДокумент переменная ДокОбъект гарантированно покидает контекст исполнения. Фиксация транзакции пачками по 100–500 документов сбрасывает накопленный журнал транзакций СУБД и очищает внутренние буферы платформы.

Решение 5. Постраничная выборка данных запросом

Если выборка документов содержит десятки тысяч записей, выгрузка запроса целиком через метод Выгрузить() приведет к созданию громоздкой таблицы значений в памяти. Вместо этого используем порционную выборку с ключевым словом ПЕРВЫЕ.

Посмотрим на пример построения порционного запроса:


ПоследняяСсылка = Документы.РеализацияТоваровУслуг.ПустаяСсылка();

Пока Истина Цикл

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 500
        |    Док.Ссылка КАК Ссылка
        |ИЗ
        |    Документ.РеализацияТоваровУслуг КАК Док
        |ГДЕ
        |    Док.Дата < &ДатаСвертки
        |    И Док.Ссылка > &ПоследняяСсылка
        |УПОРЯДОЧИТЬ ПО
        |    Док.Ссылка";

    Запрос.УстановитьПараметр("ДатаСвертки", ГраницаОчистки);
    Запрос.УстановитьПараметр("ПоследняяСсылка", ПоследняяСсылка);

    Выборка = Запрос.Выполнить().Выбрать();

    Если Выборка.Количество() = 0 Тогда
        Прервать;
    КонецЕсли;

    МассивДляОбработки = Новый Массив;
    Пока Выборка.Следующий() Цикл
        МассивДляОбработки.Добавить(Выборка.Ссылка);
        ПоследняяСсылка = Выборка.Ссылка;
    КонецЦикла;

    ОбработатьПачкуДокументов(МассивДляОбработки);

КонецЦикла;

Такой цикл не накапливает результаты запроса в памяти и позволяет обработать любой объем документов без риска падения программы по переполнению ОЗУ.

Альтернативный архитектурный подход: свертка через перенос остатков

Если конфигурация сильно модифицирована и перегружена сложными внутренними взаимосвязями, физическое удаление или каскадная отмена проведения миллионов старых записей часто оказываются нецелесообразными. В подобных ситуациях более надежным решением является классическая свертка базы:

  1. Формирование и фиксация входящих остатков на выбранную дату в текущей базе.
  2. Создание новой чистой информационной базы из конфигурации-источника.
  3. Перенос в чистую базу только актуальных справочников и сформированных документов ввода начальных остатков (с использованием типовых правил конвертации или универсальных обработок выгрузки/загрузки XML).
  4. Перевод старой базы в архивный режим только для чтения.

Данный подход исключает необходимость массового удаления миллионов записей из СУБД, устраняет дефрагментацию таблиц и обеспечивает максимальную производительность обновленной системы.

← На главную