Чёрный пояс 1С. Мёртвый кэш и утечка памяти rphost: алгоритм расследования от технологического журнала до дампа WinDbg
Рост Private Bytes у rphost без очевидной причины - одна из тех проблем, где основное время уходит не на исправление, а на локализацию: платформа не пишет в лог «у вас утечка», она просто ест память, пока кластер не прибьёт процесс сам. Ниже - инженерный алгоритм: как отделить обычный рост от утечки, где в технологическом журнале искать виновную строчку кода, что делать, если журнал ничего не показал, и какие три антипаттерна чаще всего держат «мёртвый кэш» - данные, которые уже отработали, но не могут освободиться из-за висящих ссылок.
1. Архитектура памяти 1С: рабочие процессы, сеансы и метрики
Основной исполнитель кода и потребитель памяти на сервере приложений - процесс rphost. Он исполняет серверный BSL-код, управляет контекстами вызовов, кэширует скомпилированные модули и обрабатывает фоновые задания. Ошибочно считать его единственным виновником: в кластерах с активным полнотекстовым поиском и большим числом параллельных сеансов не реже накапливает утечку rmngr - менеджер кластера, который кэширует сеансовые данные и держит блокировки; профиль его памяти стоит снимать отдельно, не только по rphost.
Для аудита разделяют три системные метрики процесса:
- Private Bytes (выделенная память): объём виртуальной памяти, запрошенный процессом у ОС и недоступный для разделения с другими процессами. Непрерывный линейный рост Private Bytes - ключевой индикатор классической утечки.
- Working Set (рабочий набор): объём страниц памяти процесса, физически находящихся в оперативной памяти в данный момент.
- Commit Limit / файл подкачки: страницы виртуальной памяти, выгруженные диспетчером памяти ОС в файл подкачки при дефиците RAM.
2. Что такое «мёртвый кэш» и типовые источники удержания
Мёртвый кэш - структуры данных и объекты платформы, которые уже завершили полезную работу, но не могут быть освобождены сборщиком из-за активных входящих ссылок: удержания контекста сеанса, глобальных переменных, невыгруженных форм или кэша модулей.
| Объект / механизм | Где возникает | Механика утечки и риски |
|---|---|---|
| Временное хранилище | Фоновые обмены, передача файлов, отчёты | Использование ПоместитьВоВременноеХранилище без передачи UUID формы - данные удерживаются в памяти до завершения сеанса. |
| Модули с повторным использованием | Общие серверные модули (кэш на время сеанса) | Передача мутабельных структур или ссылок на контексты в параметры функции; кэширование больших таблиц. |
| Управляемые формы | АРМ, открытые сутками | Накопление данных в реквизитах формы без очистки, хранение тяжёлых коллекций в клиент-серверном контексте. |
| Фоновые задания | Массовая обработка данных, регламентные загрузки | Обработка выборок в цикле без пачек (батчей), накопление транзакционного кэша объектов без периодической фиксации. |
| Циклические ссылки | Сложные вложенные структуры, деревья | Взаимные ссылки объектов друг на друга (Структура → Массив → Структура), препятствующие обнулению счётчика ссылок. |
| Пулы сеансов HTTP-сервисов | Интеграционные шлюзы, REST API | Сеанс HTTP-сервиса не уничтожается после ответа, а засыпает для переиспользования - мутабельные переменные и временное хранилище без явной очистки остаются в rphost. Особенно критично при тяжёлых JSON-пайплайнах (подготовка данных для RAG-ассистентов, поисковых API). |
| COM-объекты | Обмен с Excel, внешние коннекторы | Неконтролируемое создание Excel.Application и подобных объектов без явного вызова методов закрытия и обнуления переменных - процесс COM-сервера остаётся висеть в памяти хоста. |
3. Анализ технологического журнала: локализация мест утечек
Технологический журнал (ТЖ) позволяет точно выявить строчку BSL-кода, серверный контекст или сеанс, вызвавший аномальный расход памяти, без остановки рабочего процесса. Метрика расхода фиксируется в свойствах Memory (выделенная память за операцию, в байтах) и MemoryPeak событий CALL, SCALL и событий обращения к СУБД (DBMSSQL для MS SQL Server, DBPOSTGRS для PostgreSQL).
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
<dump create="false"/>
<log location="C:\LOGS\1C_Memory_Trace" history="48">
<!-- Фиксация серверных вызовов с расходом памяти более 100 МБ -->
<event>
<eq property="name" value="CALL"/>
<gt property="Memory" value="104857600"/>
</event>
<event>
<eq property="name" value="SCALL"/>
<gt property="Memory" value="104857600"/>
</event>
<property name="all"/>
</log>
</config>
В сформированном логе анализируются ключевые атрибуты: Context - стек вызова методов встроенного языка, где произошёл скачок выделения памяти; Usr / SessionID - пользователь или номер регламентного фонового задания; Memory / MemoryPeak - точный объём запрошенной оперативной памяти за время вызова.
4. Снятие и анализ дампов памяти процесса rphost
Если технологический журнал показывает постоянный фоновый рост памяти вне конкретных контекстных вызовов, причина может быть в накоплении объектов в куче C++ рантайма или в системной фрагментации.
Инструментарий снятия дампа:
- Windows: утилита Sysinternals
procdump.exe -ma <PID_rphost> rphost_leak.dmpили диспетчер задач. - Linux: утилита
gcore -o rphost_dump <PID_rphost>.
Анализ нативного дампа: rphost - нативное C++ приложение, поэтому применяются отладчики WinDbg, DebugDiag Analysis (Windows) или Heaptrack / GDB (Linux). Но у самостоятельного анализа есть жёсткий потолок.
- В WinDbg расширение
!address -summaryоценивает распределение страниц памяти (Heap, Stack, Image, RegionUsageVM) - это уровень ОС, символы платформы не нужны. - Команда
!heap -sоценивает размер и фрагментацию системных куч процесса - тоже работает без символов 1С. - По графу распределения удержания выявляются наиболее крупные непрерывные блоки выделений.
5. Временное хранилище: правила безопасной работы
Неправильная работа с временным хранилищем - самая частая причина мёртвого кэша в длительных пользовательских сеансах.
// АНТИПАТТЕРН: данные привязаны к сеансу,
// будут удерживаться в памяти rphost до полного выхода пользователя из системы
АдресХранилища = ПоместитьВоВременноеХранилище(ТаблицаБольшихДанных);
// ПАТТЕРН: передан УникальныйИдентификатор контекста формы -
// платформа автоматически удалит данные при закрытии серверного контекста формы
АдресХранилища = ПоместитьВоВременноеХранилище(ТаблицаБольшихДанных, ЭтаФорма.УникальныйИдентификатор);
// ПАТТЕРН: явное освобождение памяти на сервере после завершения обработки
УдалитьИзВременногоХранилища(АдресХранилища);
6. Фоновые задания и транзакционный кэш
При обработке больших объёмов данных в цикле фонового задания платформа неявно кэширует считываемые и модифицируемые объекты. Если транзакция остаётся открытой или обработка идёт единым монолитным куском, рабочий процесс быстро исчерпывает лимиты памяти. Батчинг решает проблему памяти, но код без перехвата исключений оставляет зависшую транзакцию при первой же ошибке в пачке - и это уже не утечка памяти, а Lock timeout на весь кластер. Оборачивайте батч в Попытка/Исключение с гарантированным откатом:
// Обработка массивов данных пачками (батчами) с защитой от зависшей транзакции
РазмерПачки = 500;
Счётчик = 0;
НачатьТранзакцию();
Попытка
Пока Выборка.Следующий() Цикл
Объект = Выборка.ПолучитьОбъект();
// ... модификация данных объекта ...
Объект.Записать();
Счётчик = Счётчик + 1;
Если Счётчик % РазмерПачки = 0 Тогда
ЗафиксироватьТранзакцию(); // сброс транзакционного кэша и блокировок
НачатьТранзакцию();
КонецЕсли;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ЗафиксироватьТранзакцию();
КонецЕсли;
Исключение
// Гарантированный откат - иначе зависшая транзакция держит блокировки
// и кэш до таймаута, а не до конца цикла
Если ТранзакцияАктивна() Тогда
ОтменитьТранзакцию();
КонецЕсли;
ЗаписьЖурналаРегистрации("МассоваяОбработка", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
ВызватьИсключение;
КонецПопытки;
7. Модули с повторным использованием значений
Кэш общих модулей с параметром ПовторноеИспользование = НаВремяСеанса или НаВремяВызова сохраняет возвращённые значения в памяти сеанса.
- Не передавать в параметры кэшируемых функций мутабельные типы (Структура, Соответствие, Массив, ТаблицаЗначений) - при изменении их состава кэш формирует дублирующие ветки.
- Не возвращать из кэшируемой функции ссылки на тяжёлые контексты (например, реквизиты открытых форм).
- Для сброса накопившегося кэша при обновлении нормативно-справочной информации использовать метод
ОбновитьПовторноИспользуемыеЗначения()- но осторожно под нагрузкой: массовый сброс на бою способен вызвать Cache Stampede (шквал параллельных запросов на пересчёт одних и тех же значений), просадку CPU и мнимое зависание базы вместо ожидаемого ускорения.
8. Предотвращение и архитектурный контроль
Для стабильности кластера и предотвращения аварий из-за утечек памяти - трёхуровневая стратегия:
- Ограничение лимитов на уровне кластера 1С: параметр «Безопасный расход памяти за один вызов» (например, 2-4 ГБ в зависимости от базы), параметр «Допустимый объём памяти рабочих процессов», плановый перезапуск процессов по интервалу («Интервал перезапуска рабочих процессов») - исключительно как стабилизирующая мера на время исправления дефектов в коде, не как постоянное решение.
- Автоматический аудит BSL-кода: статический анализатор (Sonarqube с плагином 1С / BSL Language Server) с правилами проверки на вызовы
ПоместитьВоВременноеХранилищебез UUID и на передачу мутабельных параметров в кэшируемые функции. - Проактивный мониторинг: алертинг (Zabbix/Prometheus) по порогу Private Bytes процесса
rphostи сбор событий технологического журнала с фильтромMemory > 100MB.
Чек-лист аудита утечек памяти
- Зафиксирован непрерывный рост Private Bytes процесса
rphostчерез системный монитор? - Настроен сбор событий
CALL/SCALLтехнологического журнала с условиемMemory > 104857600? - Все вызовы
ПоместитьВоВременноеХранилищепередаютЭтаФорма.УникальныйИдентификатор? - Функции модулей с повторным использованием не принимают мутабельные параметры?
- Циклические алгоритмы фоновых заданий разбиты на пачки с периодическим
ЗафиксироватьТранзакцию()? - Заданы параметры «Безопасный расход памяти за один вызов» в свойствах рабочего сервера кластера?
Глоссарий
- rphost - рабочий процесс сервера «1С:Предприятия», выполняющий код модулей и управляющий контекстами клиентских сеансов.
- Private Bytes - объём виртуальной памяти, выделенной процессу, недоступный для совместного использования другими приложениями.
- Working Set - объём страниц памяти процесса, находящихся непосредственно в оперативной памяти (RAM).
- Мёртвый кэш - структуры данных в оперативной памяти, потерявшие прикладную актуальность, но удерживаемые входящими ссылками.
- Memory / MemoryPeak - атрибуты событий технологического журнала 1С, фиксирующие объём оперативной памяти, запрошенный за время исполнения операции.
- Временное хранилище - платформенный механизм передачи двоичных и прикладных данных между контекстами клиента и сервера.
- WinDbg / DebugDiag - системные отладчики для низкоуровневого анализа дампов памяти нативных процессов Windows.
Создать токен →