К статье
Практический материал

Разработка и Конфигурация · 12 мин

Переполнение стека встроенного языка на сервере 1С: что это и как исправить

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

Ошибка «Переполнение стека встроенного языка на сервере» — одно из самых характерных сообщений, с которым сталкиваются разработчики 1С. Она означает, что программный код на встроенном языке 1С выполнил слишком много вложенных вызовов процедур и функций, и отведённая под стек вызовов память исчерпана. Чаще всего причина — бесконечная или слишком глубокая рекурсия, когда процедура прямо или косвенно вызывает саму себя без условия выхода.

Стек вызовов — это область памяти, в которой платформа хранит информацию о каждом незавершённом вызове: адрес возврата, локальные переменные, параметры. Каждый новый вызов процедуры или функции добавляет в стек запись, а завершение — удаляет её. Если вызовы накапливаются быстрее, чем завершаются, стек переполняется, и сервер 1С аварийно прерывает выполнение кода с соответствующим сообщением.

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

Что такое стек вызовов и почему он переполняется

Стек вызовов работает по принципу «последним пришёл — первым ушёл». Когда процедура А вызывает функцию Б, платформа запоминает, куда вернуться после завершения Б, и сохраняет её локальные данные. Если Б вызывает В, а В — снова А, цепочка растёт. Пока глубина вложенности разумна, всё работает; при тысячах или десятках тысяч вложенных вызовов память стека заканчивается.

Важно понимать: переполнение стека — это не утечка памяти и не нехватка оперативной памяти сервера в целом. Это ограничение именно на глубину вложенных вызовов внутри одного потока выполнения кода. Даже на сервере с большим объёмом ОЗУ бесконечная рекурсия гарантированно приведёт к этой ошибке.

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

Типичные причины ошибки

Самая распространённая причина — рекурсия без корректного условия выхода. Разработчик пишет процедуру обхода дерева, но забывает проверку на конец ветки, либо условие выхода никогда не наступает из-за особенностей данных: циклическая ссылка в иерархии, элемент, чей родитель указывает сам на себя или на потомка.

Вторая группа причин — косвенная рекурсия через обработчики событий. Классический сценарий: в обработчике «ПриЗаписи» документа код изменяет и записывает тот же или связанный объект, что снова инициирует «ПриЗаписи», и цепочка повторяется до переполнения стека. Аналогично зацикливаются подписки на события, когда одна подписка вызывает действие, перехватываемое другой подпиской.

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

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

Проверить условия завершения рекурсивной процедуры

Зацикливание в ПриЗаписиКосвенная рекурсия через события

Ищите запись объекта внутри его собственных обработчиков

Циклические данныеРодитель ссылается на потомка

Проверьте иерархию справочника на замкнутые петли

Взаимные вызовы модулейА вызывает Б, Б вызывает А

Трассируйте стек вызовов в технологическом журнале

Как выглядит ошибка и где искать её следы

Пользователь видит сообщение вида «Переполнение стека встроенного языка на сервере» с указанием модуля и строки, хотя указанная строка — это лишь место, где стек исчерпался, а не обязательно источник проблемы. Реальный виновник может находиться выше по цепочке вызовов.

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

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

Как найти зацикленный вызов: порядок действий

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

Затем включите технологический журнал на время воспроизведения, получите стек вызовов и найдите в нём периодически повторяющуюся последовательность модулей и процедур. Эта последовательность — цикл, который нужно разорвать.

Порядок действий
  1. 1
    Воспроизведите ошибку и зафиксируйте операцию и данные, на которых она возникает
  2. 2
    Включите технологический журнал с событием EXCP и повторите операцию
  3. 3
    Откройте стек вызовов в событии исключения и найдите повторяющуюся цепочку процедур
  4. 4
    Проверьте найденную процедуру: есть ли условие выхода и наступает ли оно на реальных данных
  5. 5
    Проверьте данные на циклические ссылки (родитель = потомок, объект ссылается сам на себя)
  6. 6
    Исправьте код или данные и повторите операцию для проверки

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

Способы исправления

Первый и главный способ — добавить или исправить условие выхода из рекурсии. Убедитесь, что на каждой итерации алгоритм приближается к завершению: обрабатываемое множество элементов уменьшается, уровень вложенности ограничен, уже посещённые узлы помечаются и пропускаются.

Для защиты от циклических данных добавьте контроль посещённых элементов: перед обработкой узла проверяйте, нет ли его в соответствии или массиве уже обработанных. Это стандартная защита от замкнутых петель в иерархии.

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

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

Сравнение вариантов

Условие выхода в рекурсии

Подходит, когда глубина вложенности ограничена и предсказуема; не защищает от циклических данных

Контроль посещённых узлов

Обязателен при обходе иерархий с возможными циклическими ссылками; немного замедляет обход

Замена рекурсии на цикл с массивом

Самый надёжный вариант для большой глубины; требует переработки алгоритма

Флаг повторного входа в обработчиках

Решает зацикливание ПриЗаписи и подписок; нужно аккуратно сбрасывать флаг

Профилактика и проверка результата

После исправления проверьте операцию на тех же данных, где возникал сбой, и на заведомо «тяжёлых» данных: максимально глубокой ветке иерархии, большом документе. Убедитесь, что операция завершается за разумное время и результат корректен, а не просто не падает.

Для профилактики при разработке рекурсивных алгоритмов сразу закладывайте ограничение глубины или контроль посещённых узлов, а в обработчиках записи — защиту от повторного входа. Полезно также тестировать обход иерархий на искусственно созданной циклической ссылке: корректный код должен завершиться без ошибки.

Контрольные точки

Как проверить результат

Операция на проблемных данных
Норма

Завершается без ошибки и даёт корректный результат

Тревожный признак

Ошибка повторяется или результат неполный

Глубокая ветка иерархии
Норма

Обход выполняется за разумное время

Тревожный признак

Операция зависает или падает на большой глубине

Циклическая ссылка в данных
Норма

Код обнаруживает петлю и завершается

Тревожный признак

Снова переполнение стека

Технологический журнал
Норма

Нет повторяющихся цепочек вызовов

Тревожный признак

В стеке виден повторяющийся цикл

Типичные ошибки при диагностике

Типичные ошибки
01
Исправлять строку из текста ошибки вместо поиска зацикленной цепочки в стеке — ошибка возвращается в другом месте
02
Игнорировать косвенную рекурсию через подписки на события — зацикливание не видно в одном модуле
03
Не проверять данные на циклические ссылки — код исправен, но падает на конкретном элементе
04
Увеличивать ресурсы сервера вместо исправления алгоритма — бесконечная рекурсия переполнит любой стек
05
Снимать защиту от повторного входа ради скорости — возврат к зацикливанию ПриЗаписи

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

Когда обращаться к специалистам

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

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

Маршрут решения

Как выбрать действие

Ошибка только на конкретном элементе данныхПроверьте этот элемент на циклические ссылки и исправьте данные
Ошибка на любых данных при одной операцииИщите рекурсию без условия выхода в коде операции
Сбой в типовой конфигурации без доработокОбновите релиз; при повторении передайте стек в поддержку вендора
Зацикливание через ПриЗаписи или подпискиДобавьте защиту от повторного входа через дополнительные свойства

Частые вопросы

Почему ошибка возникает только на одном элементе справочника?

Скорее всего, в данных этого элемента есть циклическая ссылка: например, родитель указывает на собственного потомка, и рекурсивный обход иерархии уходит в бесконечный цикл. Проверьте цепочку родителей элемента и исправьте данные, а в код добавьте контроль уже посещённых узлов.

Поможет ли увеличение памяти сервера или перезапуск служб?

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

Почему в тексте ошибки указана строка, где нет ничего подозрительного?

Указанная строка — место, где стек исчерпался в момент очередного вызова, а не источник проблемы. Истинную причину показывает стек вызовов в технологическом журнале: повторяющаяся цепочка процедур в нём и есть зацикленный участок.

Может ли переполнение стека возникнуть без рекурсии?

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

Как защитить обработчик ПриЗаписи от зацикливания?

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

Опасна ли эта ошибка для данных базы?

Сама по себе ошибка прерывает выполнение кода и транзакция откатывается, поэтому прямой порчи данных обычно нет. Риск возникает при попытках исправления: правка кода или данных в рабочей базе без копии может повредить учётные данные, поэтому все изменения выполняйте сначала на копии.

Итог

Что важно запомнить

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