Оптимизация использования оперативной памяти
Область применения: управляемое приложение, мобильное приложение, обычное приложение.
Методическая рекомендация (полезный совет)
1. Не следует разрабатывать решения исходя из неограниченного объема оперативной памяти. Для многопользовательских систем любое неэффективное использование памяти может катастрофически сказаться на работоспособности.
Следует избегать формирования больших структур данных в памяти. Если объём данных, с которыми работает бизнес-логика, сам по себе ничем не ограничен, его нужно ограничивать искусственно, обрабатывая данные порциями и сохраняя результаты в базу или файлы.
2. При потенциально неограниченных выборках данных из ИБ следует получать данные из базы порциями фиксированного размера.
Например, неправильно:
Запрос = Новый Запрос;
Запрос.Текст =
«ВЫБРАТЬ
| Номенклатура.Ссылка,
| Номенклатура.Наименование,
| Номенклатура.ВидНоменклатуры
|ИЗ
| Справочник.Номенклатура КАК Номенклатура»;
// Выгрузка всего справочника в таблицу значений
Номенклатура = Запрос.Выполнить().Выгрузить();
Для каждого ПозицияНоменклатуры Из Номенклатура Цикл
// Обработка элемента справочника
// .
КонецЦикла;
поскольку весь результат запроса сразу помещается в память, в таблицу значений.
Также неправильно:
Запрос = Новый Запрос;
Запрос.Текст =
«ВЫБРАТЬ
| Номенклатура.Ссылка,
| Номенклатура.Наименование,
| Номенклатура.ВидНоменклатуры
|ИЗ
| Справочник.Номенклатура КАК Номенклатура»;
РезультатЗапроса = Запрос.Выполнить();
// Обход результата запроса
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Обработка элемента выборки
// .
КонецЦикла;
поскольку и в этом случае при выполнении запроса его результат будет сначала считан в память целиком (*).
* Примечание. Если используется 32-битная версия платформы, и размер результата запроса превосходит размер имеющейся памяти, то данные будут записаны на диск, а затем считаны оттуда в процессе вызовов Выборка.Следующий().
Правильно ограничивать результат запроса искусственно:
ВсеОбработано = Ложь;
Пока Истина Цикл
Запрос = Новый Запрос;
Запрос.Текст =
«ВЫБРАТЬ ПЕРВЫЕ 1000
| Номенклатура.Ссылка,
| Номенклатура.Наименование,
| Номенклатура.ВидНоменклатуры
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| «;
РезультатЗапроса = Запрос.Выполнить();
ВсеОбработано = РезультатЗапроса.Пустой();
Если ВсеОбработано Тогда
Прервать;
КонецЕсли;
// Обход порции результата запроса
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Обработка элемента выборки
// .
КонецЦикла;
Выборка = Справочники.Номенклатура.Выбрать(. Отбор);
Пока Выборка.Следующий() Цикл
// Обработка элемента выборки
// .
КонецЦикла;
поскольку в этом случае платформа 1С:Предприятие выполняет получение данных из базы порциями фиксированного размера.
Кроме того, число элементов выборки автоматически ограничивает платформа 1С:Предприятие в запросах динамических списков.
3. Недопустимо работать с большими XML документами с помощью объектов встроенного языка, предназначенных для обработки файлов целиком: текстовые документы в ТекстовыйДокумент , XML в ДокументDOM и HTML в ДокументHTML , а также создавать в памяти XDTO-пакеты размером с весь XML-файл целиком.
В противном случае, весь файл загружается в оперативную память целиком. Исключения составляют отдельные случаи, когда необходим произвольный доступ к содержимому файла, к какой-то конкретной его части.
Следует использовать объекты для последовательной записи и последовательного чтения: ЧтениеXML , ЧтениеТекста , ЗаписьXML , ЗаписьТекста , с помощью которых можно прочитать файл порциями и расходовать память экономно.
При использовании механизмов XDTO неправильно зачитывать в память весь XML-файл целиком ( ФабрикаXTDO.ПрочитатьXML(ЧтениеXML) ). Вместо этого следует зачитывать XML-файл последовательно, с помощью объекта ЧтениеXML , а его отдельные фрагменты (теги) десериализовывать с помощью фабрики XDTO.
4. Другая распространенная причина неэффективное использование памяти — утечки памяти. К утечкам памяти приводит создание циклических ссылок – память выделяется и не освобождается. Например, если есть объекты, внутри которых вложены другие объекты, и где-то в глубине они ссылаются на самый верхний объект. В результате образуется циклическая ссылка.
Упрощенный пример циклической ссылки:
Данные = Новый Структура;
Данные.Вставить(«Ключ», Данные);
Следует разрывать (очищать) ссылки, когда объект становится не нужен.
Например, для примера выше:
Для выявления утечек памяти можно применять технологический журнал, включив в файл настройки параметров технологического журнала logcfg.xml элемент .
- Документация к платформе 1С:Предприятие, Приложение 3. Описание и расположение служебных файлов — logcfg.xml
- Поиск циклических ссылок
Выборка 1С
Выборка 1С – это специальный объект в 1С 8.3, который позволяет перебрать элементы справочников, документов и прочих объектов, имеющих такую возможность.
Для создания выборки справочника следует использовать методы менеджера справочника Выбрать и ВыбратьИерархически (только для справочников с иерархией), а для создания выборки документов – Выбрать.
Выборка = Документы.ПродажаТоваров.Выбрать();
Выборка = Справочники.Контрагенты.Выбрать();
Для простой выборки.
Выборка = Справочники.Регионы.ВыбратьИерархически();
Для иерархической выборки.
Поскольку выборка это объект, предназначенный для перебора элементов, то для выполнения перебора элементов следует делать обход выборки. Обход выборки осуществляется при помощи цикла. Обход выборки осуществляется динамически, это значит, что при создании выборки не все элементы выбираются сразу из базы данных, а получение элементов происходит порциями, частями. Для обхода используется метод выборки Следующий. Этот метод возвращает Истина, когда следующий элемент выбран, и Ложь, когда достигнут конец выборки.
Обойти выборку документов можно так:
Выборка = Документы.ПродажаТоваров.Выбрать(); Пока Выборка.Следующий() Цикл КонецЦикла;
А справочников, соответственно так:
Выборка = Справочники.Контрагенты.Выбрать(); Пока Выборка.Следующий() Цикл КонецЦикла;
В процессе обхода выборки, мы можем обращаться напрямую ко всем реквизитам того объекта, который выбран на текущей итерации.
Выборка = Справочники.Контрагенты.Выбрать(); Пока Выборка.Следующий() Цикл Сообщить("Наименование: " + Выборка.Наименование + ",ИНН: " + Выборка.ИНН + ",КПП: " + Выборка.КПП); КонецЦикла;
В результате, выведется информация обо всех контрагентах.
Поскольку, Контрагенты иерархический справочник, то в выборке присутствуют и группы, и элементы.
Мы не можем получить выборку только элементов или групп, поэтому, чтобы вывелись только элементы, необходимо воспользоваться стандартным реквизитом справочника ЭтоГруппа, этот реквизит имеет значение Истина, когда элемент является группой, и Ложь – когда просто элемент.
Можно код выше исправить следующим образом:
Выборка = Справочники.Контрагенты.Выбрать(); Пока Выборка.Следующий() Цикл Если Выборка.ЭтоГруппа Тогда Продолжить; КонецЕсли; Сообщить("Наименование: " + Выборка.Наименование + ",ИНН: " + Выборка.ИНН + ",КПП: " + Выборка.КПП); КонецЦикла;
Тогда выведутся только элементы.

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

У этого перечисления два значения Покупатель и Поставщик. Если требуется, чтобы выборка выше выводила только покупателей, то следует создать структуру с единственным ключом, название которого должно совпадать с названием реквизит справочника, а в значение структуры указать нужное значение перечисления.
Отбор = Новый Структура("ТипКонтрагента",Перечисления.ТипыКонтрагентов.Покупатель); Выборка = Справочники.Контрагенты.Выбрать(,,Отбор); Пока Выборка.Следующий() Цикл Сообщить("Наименование: " + Выборка.Наименование + ",ИНН: " + Выборка.ИНН + ",КПП: " + Выборка.КПП); КонецЦикла;
В этом случае будут отобраны только контрагенты-покупатели.

Обратите внимание, что в случае установки отбора нет смысла отсекать группы. Они не попадут в выборку, поскольку этот реквизит у них не заполнен.
Также, имейте в виду, что отбор можно производить только по стандартным реквизитам Наименование, Код, а также по тем реквизитам, у которых в свойстве Индексировать установлено значение Индексировать или Индексировать с доп. Упорядочиванием.
Иерархическая выборка справочников 1С
По мимо обычной выборки справочников при помощи метода менеджера справочника Выбрать, можно производить иерархическую выборку при помощи метода ВыбратьИерархически. Чем отличается простая выборка справочника от иерархической? Разберем простой пример. Выведем в таблицу значений на форме элементы и группы справочника номенклатуры обычной выборкой, а потом иерархической.

Для обычной выборки будет следующий код:
Выборка = Справочники.Номенклатура.Выбрать(); Пока Выборка.Следующий() Цикл НовСтр = ТЗНоменклатура.Добавить(); ЗаполнитьЗначенияСвойств(НовСтр,Выборка); КонецЦикла;
И следующий результат:

Все элементы и группы вывелись как попало, при следующей структуре справочника:

А теперь посмотрим, как будут выведены группы и элементы при иерархической выборке.
Выборка = Справочники.Номенклатура.ВыбратьИерархически(); Пока Выборка.Следующий() Цикл НовСтр = ТЗНоменклатура.Добавить(); ЗаполнитьЗначенияСвойств(НовСтр,Выборка); КонецЦикла;
Получится следующий результат:

Элементы вывелись в иерархическом порядке. Иерархическая выборка перебирает элементы следующем образом: сначала выбирается элемент самого верхнего уровня, потом если это группа, то отбирается элемент, который входит в эту группу и т.д.
А если мы воспользуемся методом выборки справочников УровеньВВыборке, то сможем увидеть порядок вложенности.
Выборка = Справочники.Номенклатура.ВыбратьИерархически(); Пока Выборка.Следующий() Цикл НовСтр = ТЗНоменклатура.Добавить(); ЗаполнитьЗначенияСвойств(НовСтр,Выборка); НовСтр.Уровень = Выборка.УровеньВВыборке() КонецЦикла;
Будет следующий результат:

Где 0 – самый высший уровень (элемент или группа не входят ни в какие группы), 1 – следующий уровень (элемент или группа входят в группу высшего уровня) и т.д.
При иерархической выборке также можно сделать отбор, но будут выбраны только элементы верхнего уровня, поскольку группы отобраны не будут (у них этот реквизит не заполнен), а если группы не отобраны, то и элементы других уровней тоже не будут выбраны, даже если они подходят по условию.
Метод ПолучитьОбъект выборки
Выборка возвращает только ссылку на объект, сам объект мы изменять не можем. Мы можем обратиться к экземпляру объекта отобранной выборкой ссылкой, если воспользуемся методом ПолучитьОбъект().
Например, в этом коде пытаемся всем элементам справочника Номенклатура с видом Товары, обнулить этот вид, присвоив пустую ссылку перечисления.
Выборка = Справочники.Номенклатура.Выбрать(,,Отбор); Пока Выборка.Следующий() Цикл ОбъектСправочник = Выборка.ПолучитьОбъект(); ОбъектСправочник.ВидНоменклатуры = Перечисления.ВидыНоменклатуры.ПустаяСсылка(); Попытка ОбъектСправочник.Записать(); Исключение Сообщить("Не удалось изменить вид"); КонецПопытки; КонецЦикла;
Для записи элемента использовалась попытка.
Читайте также по теме:
Более подробно и основательно начальные вопросы программирования в 1С есть вы можете изучить в
Книга «Программировать в 1С за 11 шагов»
Изучайте программирование в 1С в месте с моей книгой «Программировать в 1С за 11 шагов»

Книга написана понятным и простым языком — для новичка.
- Книга посылается на электронную почту в формате PDF. Можно открыть на любом устройстве!
- Научитесь понимать архитектуру 1С;
- Станете писать код на языке 1С;
- Освоите основные приемы программирования;
- Закрепите полученные знания при помощи задачника
О том как разрабатывать под управляемым приложением 1С, читайте в книге Книга «Основы разработки в 1С: Такси»
Отличное пособие по разработке в управляемом приложении 1С, как для начинающих разработчиков, так и для опытных программистов.

- Очень доступный и понятный язык изложения
- Книга посылается на электронную почту в формате PDF. Можно открыть на любом устройстве!
- Поймете идеологию управляемого приложения 1С
- Узнаете, как разрабатывать управляемое приложение;
- Научитесь разрабатывать управляемые формы 1С;
- Сможете работать с основными и нужными элементами управляемых форм
- Программирование под управляемым приложением станет понятным
Промо-код на скидку в 15% — 48PVXHeYu
Эти книги, плюс книга по программированию оперативного учета имеются в едином комплекте: комплект книг по разработке в 1С.
Только для читателей моего блога,
промо-код на скидку в 300 рублей на весь комплект: blog
Если Вам понравился этот урок, был полезен или помог решить какую-нибудь проблему, то Вы можете поддержать мой проект, перечислив любую сумму.
можно оплатить вручную:
И вступайте в мои группы:
Как выбрать данные в 1с порциями
Запрос = Новый Запрос;
Запрос.Текст =
«ВЫБРАТЬ ПЕРВЫЕ 100
| Банки.Код КАК Код,
| Банки.Ссылка
|ИЗ
| Справочник.Банки КАК Банки
|ГДЕ
| Банки.Код > &Код
|
|УПОРЯДОЧИТЬ ПО
| Код»;
Запрос.Текст=СтрЗаменить(Запрос.Текст,»Банки11″,Справочник);
Запрос.УстановитьПараметр(«Код», «0»);
Цк=Истина;
Пока Цк Цикл
Тб = Запрос.Выполнить().Выгрузить();
Если Тб.Количество() > 0 Тогда
Для каждого Эл из Тб Цикл
// Что-то делаем
КонецЦикла;
Запрос.УстановитьПараметр(«Код», Эл.Код);
Иначе
Цк=ложь;
КонецЕсли;
КонецЦикла;
1С. Удаление записей регистра сведений
Разберем примеры удаления записей из регистра сведений «Данные для обработки» некой конфигурации, регистр имеет одно измерение «Номенклатура«, тип Справочник.Номенклатура.
Создание и запись пустого набора
Заголовок говорит сам за себя:
&НаСервере Процедура УдалитьЗаписиНаСервере() НаборЗаписей = РегистрыСведений.ДанныеДляОбработки.СоздатьНаборЗаписей(); НаборЗаписей.Записать(Истина); КонецПроцедуры
При большом количестве записей будет расходоваться оперативная память и образуется очередь на диск.
Чтение и запись порциями
Просто берем из регистра записи порциями и обрабатываем, пока записи не кончатся:
&НаСервере Процедура УдалитьЗаписиНаСервере() Продолжать = Истина; Пока Продолжать Цикл Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1000 | ДанныеДляОбработки.Номенклатура |ИЗ | РегистрСведений.ДанныеДляОбработки КАК ДанныеДляОбработки"; Продолжать = Не Запрос.Выполнить().Пустой(); Выборка = Запрос.Выполнить().Выбрать(); Пока Выборка.Следующий() Цикл НаборЗаписей = РегистрыСведений.ДанныеДляОбработки.СоздатьНаборЗаписей(); НаборЗаписей.Отбор.Номенклатура.Установить(Выборка.Номенклатура); НаборЗаписей.Записать(Истина); КонецЦикла; КонецЦикла; КонецПроцедуры
При таком варианте расходование оперативной памяти будет более рациональным, но будет много операций записи
Чтение и запись порциями в транзакциях
По сути берется предыдущий вариант, но цикл обхода записей заключаем в транзакцию:
&НаСервере Процедура УдалитьЗаписиНаСервере() Сообщение = Новый СообщениеПользователю; Сообщение.Текст = "" + ТекущаяДата() + " Начало"; Сообщение.Сообщить(); Продолжать = Истина; Пока Продолжать Цикл Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1000 | ДанныеДляОбработки.Номенклатура |ИЗ | РегистрСведений.ДанныеДляОбработки КАК ДанныеДляОбработки"; Продолжать = Не Запрос.Выполнить().Пустой(); Выборка = Запрос.Выполнить().Выбрать(); НачатьТранзакцию(); Пока Выборка.Следующий() Цикл НаборЗаписей = РегистрыСведений.ДанныеДляОбработки.СоздатьНаборЗаписей(); НаборЗаписей.Отбор.Номенклатура.Установить(Выборка.Номенклатура); НаборЗаписей.Записать(Истина); КонецЦикла; ЗафиксироватьТранзакцию(); КонецЦикла; Сообщение = Новый СообщениеПользователю; Сообщение.Текст = "" + ТекущаяДата() + " Завершение"; Сообщение.Сообщить(); КонецПроцедуры
Таким образом мы уменьшим количество записей в БД.
Сравнение
Сравним скорость удаления 1 000 000 записей порциями по 1 000 записей с использованием транзакций и без них. Используется файловая ИБ расположенная на жестком диске SATA 3(Специально используется жестки диск, а не SSD для более выраженного различия).
Удаление порциями: 25 минут 12 секунд
Удаление порциями с транзакциями: 15 минут 43 секунды
