Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
former_member184455
Active Participant

ABAP offers a great bunch of dynamic programming techniques, ranging from anonymous data objects to dynamic Open SQL statements, program generation, and others.

Since this is a performance blog, I recommend that you use all these different techniques only if you really need them. Many checks that are executed at compile time for static code have to be postponed until runtime and are then executed repeatedly for every execution when you use dynamic programming. As a result, the performance of dynamic program sections is always poorer than that of static ones.

Today I want to discuss a memory problem that can occur if ABAP Runtime Type Creation (RTTC) is used extensively. Sometimes you need to provide code that is capable of working with internal tables that have a line structure which is not known at design time. In such a case it is convenient to create the internal tables dynamically at runtime.

In the following code snippet, I first create a simple structure type my_struct dynamically, and then from this structure type, a table type my_table. This is done using the CREATE methods of ABAP Runtime Type Services (RTTS) classes CL_ABAP_STRUCTDESCR and CL_ABAP_TABLEDESCR. At the end, an internal table is created with the CREATE DATA statement: 

DATA: comp_tab TYPE cl_abap_structdescr=>component_table,
         comp          LIKE LINE OF comp_tab,
         my_struct    TYPE REF TO cl_abap_structdescr,
         my_table     TYPE REF TO cl_abap_tabledescr,
         tab_handle  TYPE REF TO data.

comp-name = 'FIRST_COL'.
comp-type = cl_abap_elemdescr=>get_i( ).
APPEND comp TO comp_tab.

comp-name = 'SECOND_COL'.
comp-type = cl_abap_elemdescr=>get_c( 10 ).
APPEND comp TO comp_tab.

TRY.
    my_struct = cl_abap_structdescr=>create( comp_tab ).
    my_table  = cl_abap_tabledescr=>create( p_line_type = my_struct ).
  CATCH: cx_sy_struct_creation, cx_sy_table_creation.
ENDTRY.

CREATE DATA tab_handle TYPE HANDLE my_table.

Now, in a real program, such generic code might be executed many times, and one often observes that the identical structure type or table type is created over and over again.

To simulate this, I put the table type creation into a DO … ENDDO loop, see code below.

DATA: comp_tab TYPE cl_abap_structdescr=>component_table,
         comp     LIKE LINE OF comp_tab,
         my_struct    TYPE REF TO cl_abap_structdescr,
         my_table     TYPE REF TO cl_abap_tabledescr,
         tab_handle   TYPE REF TO data.

comp-name = 'FIRST_COL'.
comp-type = cl_abap_elemdescr=>get_i( ).
APPEND comp TO comp_tab.

comp-name = 'SECOND_COL'.
comp-type = cl_abap_elemdescr=>get_c( 10 ).
APPEND comp TO comp_tab.
my_struct = cl_abap_structdescr=>create( comp_tab ).

DO 10000 TIMES.

  my_table = cl_abap_tabledescr=>create( p_line_type = my_struct ).

ENDDO.

BREAK-POINT.

When the program reaches the BREAK-POINT statement, I get into the debugger and open the memory analysis tool. In my first compilation of screen shots you find that ≈ 969 kB of 'ABAP Application' memory is allocated by 'Dyn. Memory Objects', most of it (≈ 963 kB) by the internal table ADMIN_TAB of RTTS class CL_ABAP_TYPEDESCR. During the execution of my little program this table grows up to 10,003 entries. What is even more important: the overall allocated memory (line 'Allocated' in area 'Total' below) is around 12 MB!

What happened? While I repeatedly created table types with an identical line structure, always a new entry was entered into the ADMIN_TAB table of the class CL_ABAP_TABLEDESCR. But where does the high amount of 'Total' memory come from? This memory is allocated by the ABAP kernel and most of it corresponds to memory objects that represent the numerous table types I created in my ABAP program. The kernel memory is not under control of the ABAP garbage collector, and often is only released at the end of the user session.   

You can easily imagine, that when a program is making massive use of dynamic type generation, the memory allocated by the internal table ADMIN_TAB and the associated kernel objects can become very large. In fact, at one SAP customer, the ABAP extended memory was blown up to its limits - with the result of short dumps due to memory overflow.

Besides the CREATE method of CL_ABAP_TABLEDESCR, there are other critical methods:
CL_ABAP_TABLEDESCR, methods CREATE and CREATE_WITH_KEYS
CL_ABAP_STRUCTDESCR, method CREATE
CL_ABAP_REFDESCR, methods CREATE and CREATE_BY_NAME

“Is there any hope?”, you might ask now, and yes, there is:
Since release NW 7.02 there also exist corresponding GET methods that make use of an internal cache. These GET methods are therefore much more efficient with respect to memory consumption, as well as with respect to CPU consumption.

I repeated the execution of my program above, but this time the following statement was inside the DO … ENDDO loop:

 my_table = cl_abap_tabledescr=>get( p_line_type = my_struct ).

See the effect on the memory allocation in the second compilation of screen shots below:

As you see, the memory allocated for ‘Dyn. Memory Objects' is only ≈ 7 kB, and the size of the internal table \CLASS=CL_ABAP_TYPEDESCR\DATA=ADMIN_TAB is down to ≈ 600 Bytes for 4 entries. The 'Total Allocated' memory is now only 4 MB - which is the default size for an initial user session. The 'Used' memory has reduced from nearly 10 MB to 1.7 MB.

So while the CREATE methods always create a new type object on every invocation, the GET methods are able to do a reuse for already existing type objects. This only holds true for type objects that were created with a GET method call. Type objects created with CREATE cannot be reused by an invocation of the GET method.

If you call the GET method repeatedly with identical parameters, it is guaranteed that the returned type objects are at least 'move compatible' with each other – that is, a MOVE between data objects created from these type objects will always be possible (this is the kind of compatibility that can also be checked with method APPLIES_TO_DATA of the RTTS classes). A higher degree of compatibility is neither guaranteed by the SAP NetWeaver layer, nor needed by a normal consumer.

So my recommendation for you is: if you really need to create a lot of type objects (structure types, table types, or reference types), and there is a high probability of identical type objects, use the appropriate GET method of the RTTC class instead of CREATE.

10 Comments