Начиная с версии Ehp2 7.0 (ABAP Kernel 7.20) в ABAP появилась возможность использования так называемых упакованных компонентов. Объявить структуру с использованием упакованных компонентов мы можем как локально, с помощью ключевого слова BOXED, так и в словаре, выбрав тип типизации:
В роли упакованных компонентов могут выступать как простые структуры, так и глубокие структуры (содержащие набор компонентов или другие вложенные структуры), а так же структурные атрибуты классов и интерфейсов. При копировании структур с помощью оператора INCLUDE TYPE|STRUCTURE атрибут BOXED копируется.
Упакованные компоненты позволяют сократить объем выделенной памяти в программе. Достигается это за счёт того что под упакованные компоненты память выделяется только при их реальном использовании (см. ниже). Для обычных же компонентов объявленных через оператор TYPE (не TYPE REF TO), память выделяется автоматически при их инициализации.
Согласно документации, выделение памяти под упакованные компоненты происходит в следующих случаях:
- Во время записи
- Во время присвоения ссылочному полю (Field Symbols)
- Когда компонент передается в любую из подпрограмм (процедуры и т.п.)
- Когда мы получаем доступ через ссылочную переменною (GET REFERENCE OF)
Операторы CLEAR и FREE не возвращают упакованные компоненты в первоначальное состояние (не сбрасывают выделенную память), а только приводят их компоненты в исходное (initial) состояние.
Пример выделения памяти для структур с разными типами и ссылочной переменной:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
REPORT zboxed_components. TYPES: BEGIN OF ty_mara, mara TYPE mara, END OF ty_mara, BEGIN OF ty_mara_boxed, mara TYPE mara BOXED, END OF ty_mara_boxed. DATA: ls_mara TYPE ty_mara, ls_mara_boxed TYPE ty_mara_boxed, ls_ref_to_mara TYPE REF TO ty_mara. BREAK-POINT. |
В результате система выделит память следующим образом:
Для упакованного компонента система выделяет 8 байт для хранения ссылки на оригинальную структуру.
В качестве еще одного примера рассмотрим небольшую программу для получения данных о поставщиках и их телефонных номерах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
REPORT zboxed_components. TABLES: lfa1. TYPES: " Структура с телефонными данными поставщиков BEGIN OF ty_data, telf1 TYPE lfa1-telf1, telf2 TYPE lfa1-telf2, END OF ty_data, " Структура без упакованного компонента BEGIN OF ty_customer, lifnr TYPE lifnr, telef TYPE ty_data, END OF ty_customer, " Структура с упакованным компонентом BEGIN OF ty_customer_boxed, lifnr TYPE lifnr, telef TYPE ty_data BOXED, END OF ty_customer_boxed. DATA: lt_tab TYPE TABLE OF ty_customer, lt_tab_b TYPE TABLE OF ty_customer_boxed. FIELD-SYMBOLS: <fs_boxed_line> TYPE ty_customer_boxed. SELECT lifnr telf1 telf2 FROM lfa1 UP TO 100 ROWS INTO TABLE lt_tab. " Не сработает т.к. OPEN SQL поддерживает только плоские структуры, " ссылочные переменные или внутренние таблицы не поддерживаются * SELECT lifnr telf1 telf2 FROM lfa1 UP TO 100 ROWS INTO TABLE lt_tab_b. " Заполним другим способом: SELECT lifnr telf1 telf2 FROM lfa1 UP TO 100 ROWS INTO lfa1. APPEND INITIAL LINE TO lt_tab_b ASSIGNING <fs_boxed_line>. MOVE-CORRESPONDING lfa1 TO <fs_boxed_line>. ENDSELECT. BREAK-POINT. |
В тестовых данных моей системы существуют такие поставщики, у которых нет информации о телефонных номерах, соответственно выделение памяти под эти данные не имеет смысла. После заполнения внутренних таблиц мы имеем следующий результат по выделению памяти:
Как видно из рисунка, после выполнения выборки, под вторую таблицу выделено гораздо меньше памяти, за счёт использования упакованных компонентов. Кроме того что использование упакованных компонентов может снизить объем выделенной памяти (в тяжелых случаях избавить от дампов при выделении памяти 🙂 ), их использование так же может ускорить время выполнения программы, за счёт копирования только внутренней ссылки, без копирования дополнительных данных, при операциях над структурными переменными.
Пример того как упакованные компоненты могут быть использованы в классах:
1 2 3 4 5 6 |
CLASS lcl_order DEFINITION. PUBLIC SECTION. ... DATA: post_addr TYPE address, alt_post_addr TYPE address BOXED. ENDCLASS. |
Видео на тему:
я не абапер, только учусь… но походу это неправильно работает и память меньше, потому что таблица с пустыми записями…
Точнее не с пустыми, а в поле lifnr пишется telf*. Как это использовать на практике я не понял. А если lt_tab_b будет заполнена такими же значениями как lt_tab, то памяти она использует больше.
Спасибо, пример действительно кривой, ниже более подходящий.
Зайдя в инструмент анализа памяти можно увидеть как относительно не пустых записей будут созданы ссылочные переменные.
TABLES: lfa1.
TYPES:
» Структура с телефонными данными поставщиков
BEGIN OF ty_data,
telf1 TYPE lfa1-telf1,
telf2 TYPE lfa1-telf2,
END OF ty_data,
» Структура без упакованного компонента
BEGIN OF ty_customer,
lifnr TYPE lifnr,
telef TYPE ty_data,
END OF ty_customer,
» Структура с упакованным компонентом
BEGIN OF ty_customer_boxed,
lifnr TYPE lifnr,
telef TYPE ty_data BOXED,
END OF ty_customer_boxed.
DATA:
lt_tab TYPE TABLE OF ty_customer,
lt_tab_b TYPE TABLE OF ty_customer_boxed.
SELECT lifnr telf1 telf2 FROM lfa1 INTO CORRESPONDING FIELDS OF lfa1.
APPEND INITIAL LINE TO lt_tab ASSIGNING FIELD-SYMBOL().
-lifnr = lfa1-lifnr.
-telef-telf1 = lfa1-telf1.
-telef-telf2 = lfa1-telf2.
APPEND INITIAL LINE TO lt_tab_b ASSIGNING FIELD-SYMBOL().
-lifnr = lfa1-lifnr.
IF lfa1-telf1 IS NOT INITIAL.
-telef-telf1 = lfa1-telf1.
ENDIF.
IF lfa1-telf2 IS NOT INITIAL.
-telef-telf2 = lfa1-telf2.
ENDIF.
ENDSELECT.
Михаил, спасибо. Пример рабочий, объем используемой памяти сокращается!