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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Каждый разработчик 1С рано или поздно сталкивается с задачей массового изменения данных: обновлением реквизитов контрагентов, перезаписью контактной информации или актуализацией цен. Сценарий часто выглядит одинаково: пишется простая обработка, которая выбирает 100 000+ объектов, в цикле модифицирует их и записывает. Однако через 10–15 минут работы процесс прерывается аварийным сообщением: «Неисправимая ошибка. Приложение будет закрыто», а в технологическом журнале фиксируется лишь лаконичное Outgoing connection closed.

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

Выясняем причину: лимиты памяти и 32-битный rphost

Проанализируем механику возникновения сбоя. Когда рабочие процессы сервера приложений 1С работают в 32-разрядном режиме, они подчиняются жестким архитектурным ограничениям операционной системы. Максимальный объем виртуального адресного пространства, доступный одному 32-битному процессу rphost.exe, составляет 2 ГБ (в редких случаях, при использовании специальных ключей операционной системы — до 3 ГБ).

Когда в процессе выполнения серверного вызова объем выделенной памяти приближается к этому критическому порогу, операционная система отказывает процессу в очередном выделении блока памяти из кучи (heap). Происходит критическая ошибка нехватки памяти (Out of Memory). Платформа 1С не может обработать это исключение в штатном режиме, из-за чего рабочий процесс rphost.exe мгновенно прекращает свою работу («падает»). Сетевое соединение между клиентом и сервером рвется, что регистрируется в технологическом журнале как закрытие исходящего соединения, а пользователь видит окно с сообщением о неисправимой ошибке.

Переход на 64-битный сервер — решение или временный костыль?

Переход на 64-разрядную версию сервера 1С полностью решает проблему жесткого лимита в 3 ГБ. В 64-битной архитектуре адресное пространство процесса практически неограниченно, и rphost.exe может задействовать всю оперативную память, доступную на физическом сервере.

Однако рассмотрим ситуацию глубже. Если алгоритм обработки написан неоптимально и допускает утечку памяти, то на 64-битном сервере процесс не упадет на 3 ГБ. Он продолжит расти, потребляя 8, 16 или даже 32 ГБ памяти. Это приведет к следующим негативным последствиям:

  1. Замедление работы всей системы из-за активного использования файла подкачки (свопинга) операционной системой.
  2. Вытеснение из оперативной памяти кэша СУБД и кэша самой платформы 1С.
  3. Аварийное завершение других рабочих процессов или зависание всего сервера при полном исчерпании физической памяти.

Таким образом, использование 64-разрядного сервера обязательно для современных систем, но оно не отменяет необходимости оптимизации кода.

Почему утекает память при обработке объектов в цикле?

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

  1. Отсутствие промежуточного освобождения памяти внутри транзакции и серверного вызова. Платформа 1С использует автоматический сборщик мусора. Он наиболее эффективно очищает память при выходе из контекста серверной процедуры или функции. Если вся обработка 100 000 объектов выполняется внутри одного непрерывного серверного вызова, созданные в памяти объекты и временные структуры продолжают удерживаться платформой.
  2. Кэширование данных. При вызове метода ПолучитьОбъект() платформа считывает данные из базы и кэширует структуру объекта в памяти текущего сеанса. При обработке огромного массива данных этот кэш разрастается до гигантских размеров.
  3. Сложная бизнес-логика при записи (БСП). В типовых конфигурациях (ЕРП, КА, УТ, БП) при записи таких объектов, как контрагенты, срабатывают десятки подписок на события. Запускаются механизмы валидации данных, версионирования, регистрации изменений для планов обмена, актуализации индексов полнотекстового поиска. Каждый такой обработчик порождает множество временных таблиц значений, структур и соответствий, которые засоряют память процесса.

Пошаговое руководство по оптимизации кода массовой обработки

Разберем детально, как правильно переписать алгоритм обработки данных, чтобы он работал быстро и не приводил к перерасходу ресурсов сервера.

Шаг 1: Порционность (пакетная обработка данных)

Никогда не выбирайте все 100 000 объектов в один массив или выборку запроса. Данные необходимо обрабатывать небольшими порциями (пакетами) по 500–1000 штук. Для этого в запросе используется конструкция ВЫБРАТЬ ПЕРВЫЕ.

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


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

Шаг 2: Разделение серверных вызовов

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

Рассмотрим структуру клиент-серверного взаимодействия:


&НаКлиенте
Процедура ЗапуститьОбработкуДанных()
    
    КоличествоОбработанных = 0;
    ПараметрыЦикла = Новый Структура("ШагСекунд, МаксимумПорции", 5, 1000);
    
    Пока Истина Цикл
        
        // Каждый вызов этой функции — отдельный контекстный или бесконтекстный вызов сервера
        ОбработаноЗаИтерацию = ОбработатьПорциюНаСервере(ПараметрыЦикла.МаксимумПорции);
        
        Если ОбработаноЗаИтерацию = 0 Тогда
            Прервать; // Все объекты успешно обработаны
        КонецЕсли;
        
        КоличествоОбработанных = КоличествоОбработанных + ОбработаноЗаИтерацию;
        Состояние("Обработано объектов: " + КоличествоОбработанных);
        
    КонецЦикла;
    
    ПоказатьПредупреждение(, "Обработка успешно завершена!");
    
КонецПроцедуры

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


&НаСервереБезКонтекста
Функция ОбработатьПорциюНаСервере(Знач РазмерПорции)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ " + Формат(РазмерПорции, "ЧГ=0") + "
        |	Контрагенты.Ссылка КАК Ссылка
        |ИЗ
        |	Справочник.Контрагенты КАК Контрагенты
        |ГДЕ
        |	Контрагенты.ДополнительныйРеквизит = &ПустоеЗначение
        |УПОРЯДОЧИТЬ ПО
        |	Ссылка";
    
    Запрос.УстановитьПараметр("ПустоеЗначение", "");
    РезультатЗапроса = Запрос.Выполнить();
    
    Если РезультатЗапроса.Пустой() Тогда
        Возврат 0;
    КонецЕсли;
    
    Выборка = РезультатЗапроса.Выбрать();
    Обработано = 0;
    
    // Оборачиваем обработку одной порции в транзакцию
    НачатьТранзакцию();
    Попытка
        Пока Выборка.Следующий() Цикл
            
            ОбъектКонтрагент = Выборка.Ссылка.ПолучитьОбъект();
            
            // Отключаем обработчики бизнес-логики БСП для ускорения и экономии памяти
            ОбъектКонтрагент.ОбменДанными.Загрузка = Истина;
            
            // Модифицируем данные
            ОбъектКонтрагент.ДополнительныйРеквизит = "Обработано";
            
            ОбъектКонтрагент.Записать();
            Обработано = Обработано + 1;
            
            // Явно освобождаем ссылку на тяжелый объект
            ОбъектКонтрагент = Неопределено;
            
        КонецЦикла;
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменятьТранзакцию = Истина;
        ОтменитьТранзакцию();
        ЗаписьЖурналаРегистрации("МассоваяОбработка", УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        ВызватьИсключение "Ошибка при записи объекта в транзакции: " + ОписаниеОшибки();
    КонецПопытки;
    
    Возврат Обработано;
    
КонецФункции

Шаг 3: Использование режима загрузки данных

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

При этом автоматически отключаются:

Это не только снижает нагрузку на оперативную память rphost, но и ускоряет запись каждого объекта в 5–10 раз.

Шаг 4: Явное высвобождение памяти

Внутри итератора цикла обязательно обнуляйте переменные, содержащие тяжелые ссылочные объекты платформы. Присвоение значения Неопределено переменной ОбъектКонтрагент сразу после выполнения метода Записать() явно указывает платформе, что объект больше не используется в коде, позволяя сборщику мусора своевременно очистить оперативную память, занятую этим объектом в куче процесса.

Оптимизация работы с контактной информацией

Если цель вашей обработки заключается в обновлении контактной информации контрагентов (адресов, телефонов, email), получение тяжелого объекта СправочникОбъект через метод ПолучитьОбъект() является избыточным действием.

В современных конфигурациях вся контактная информация хранится во в табличных частях или в специализированных регистрах сведений (например, в регистре СоставТочекКонтакта). Рассмотрите возможность записи данных напрямую в соответствующий регистр сведений через создание набора записей РегистрСведенийНаборЗаписей. Работа с наборами записей регистров напрямую минует логику справочников и потребляет в десятки раз меньше оперативной памяти.

Настройки на стороне кластера серверов 1С

Для предотвращения аварийных ситуаций на сервере приложений при запуске масштабных обработок администраторам рекомендуется выполнить следующие настройки в консоли администрирования кластера серверов 1С:

Используя приведенные архитектурные подходы и правила написания кода, мы сможем гарантировать стабильную работу сервера 1С без утечек памяти и досадных аварийных завершений системы даже при обработке многомиллионных массивов данных.

← На главную