Note: a more suitable name for this blog should be “DELETE itab statement and memory use” or “DELETE itab statement and memory deallocation”, because the Garbage Collector is not responsible for internal tables memory allocation. Thank Randolf Eilenberger for alerting me about this (see comments).
Hi All,
This week I had a problem regarding memory use in an ABAP program. While debugging the program with the Memory Analysis tool active, I found something that intrigues me. After a DELETE itab statement where 60% of the rows were deleted, instead of decreasing the memory use what happened was an increase of the memory use.
I never had the curiosity of analize the memory use behavior of program after a DELETE statement because I really believed that the DELETE statement and memory release were like synonyms. But unfortunately I was completely wrong.
Let me show you an example that i created for this blog.
before DELETE
after DELETE
As we can see, the memory used increased after the DELETE (note that the number of rows was reduced dramatically). So I decided to investigate it, and that’s why I’m here, writing this blog.
After a few tests I found a way to release the memory, by transfering the remaining rows to a auxiliary internal table, clearing the original internal table and transfering the content back to the original.
Let me show you the results.
As expected, at first we have an increase in the memory use
but after clearing the original table the memory use is reduced dramatically
I would like to know from the community if you have already noticed this behavior of the ABAP garbage collector.
Below is the source code of a method to release the memory allocated to an internal table (that is intended to be used after a delete of a reasonable number of rows).
Method Definition
METHODS: release_memory CHANGING ch_t_tab TYPE STANDARD TABLE.
Method Implementation
METHOD release_memory.
FIELD-SYMBOLS: <fs_table> TYPE STANDARD TABLE,
<fs_line> TYPE ANY,
<fs_tab_line> TYPE ANY.
DATA: lo_struct_typ TYPE REF TO cl_abap_structdescr,
lo_dyntable_typ TYPE REF TO cl_abap_tabledescr,
lt_dyntable TYPE REF TO data,
ls_dyntable TYPE REF TO data.
READ TABLE ch_t_tab INDEX 1
ASSIGNING <fs_tab_line>.
lo_struct_typ ?= cl_abap_typedescr=>describe_by_data( <fs_tab_line> ).
lo_dyntable_typ = cl_abap_tabledescr=>create( p_line_type = lo_struct_typ ).
CREATE DATA: lt_dyntable TYPE HANDLE lo_dyntable_typ,
ls_dyntable TYPE HANDLE lo_struct_typ.
ASSIGN: ls_dyntable->* TO <fs_line>,
lt_dyntable->* TO <fs_table>.
LOOP AT ch_t_tab ASSIGNING <fs_tab_line>.
MOVE-CORRESPONDING <fs_tab_line> TO <fs_line>.
APPEND <fs_line> TO <fs_table>.
ENDLOOP.
FREE ch_t_tab.
ch_t_tab[] = <fs_table>.
ENDMETHOD. "release_memory
Updated with a new example with the suggestion to call the garbage collector manually.
Result: starting the garbage collector after the DELETE statement doesn’t release memory, in fact it increase the memory used a little bit more…
Here’s a new example.
Before DELETE
After DELETE
After Garbage Collection
After release memory method
Here’s a graph of another execution.
Regards,
Christian
Hi Christian,
I knew this FREE makes free something and it has to be used! 🙂
Initializing Internal Tables
Hi Tuncay,
In fact, the point here is the DELETE statement that doesn’t release memory.
Regards,
Christian
Hi Christian,
I got it.
This is what I meant DELETE doesn’t release memory, but FREE does!
Thanks,
Tuncay
Well, FREE certainly frees up memory (it’s in the documentation).
I believe garbage collector runs only periodically, so you might not see an instant effect. Have you tried starting garbage collector manually?
CL_ABAP_MEMORY_UTILITIES=>DO_GARBAGE_COLLECTION( ).
Blog updated with the results of the suggestion of calling the garbage collector manually.
Now it becomes even more interesting. Starting the garbage collector after the DELETE statement doesn’t release memory, in fact it increase the memory used a little bit more…
Regards,
Christian
Hi Christian,
The observed effects have nothing to do with the garbage collector – the GC is simply not responsible for the memory allocated by internal tables.
Also, the DELETE statement on internal tables does not release any allocated memory. This can only be achieved with the FREE, or the CLEAR or REFRESH statements.
The story would be even more puzzling, if internal table LT_EKKO_AUX is not filled with LOOP + APPEND, as in your example, but instead with a simple
> LT_EKKO_AUX = LT_EKKO.
This way, the allocated memory would not reduce at all after the sequence
> FREE LT_EKKO.
> LT_EKKO = LT_EKKO_AUX.
> FREE LT_EKKO_AUX.
The reason is the table sharing, that includes all allocated table pages – even if they are empty.
Best Regards,
Randolf
Hi Randolf,
My first test was exactly this one, moving all content using lt_ekko_aux[] = lt_ekko[], that didn’t work. Searching the documentation I found the following explanation:
Reference: http://help.sap.com/saphelp_nw70ehp2/helpdata/en/88/84fb5269d34838a1f119b41dcdbc57/frameset.htm
Once the garbage collector is not responsible for the memory allocated by internal tables and the DELETE statement doesn’t release memory, if we need to release memory, we have to do manually right? (like I did)
Thanks for your feedback.
Regards,
Christian
Hi Christian,
Yes, long living internal tables should be FREEd or CLEARed if their content is no longer needed. On the other hand, for a local variable the memory will be released automatically when the procedure is left.
Best Regards,
Randolf
Hello Christian,
First of all thanks for such a nice blog.
I just want to add that, to over come the Lazy copy strategy, instead of using a LOOP. ENDLOOP. you can also use following way
LT_EKKO_AUX = LT_EKKO.
APPEND INITIAL LINE TO LT_EKKO_AUX.
This will be enough to force ABAP to allocate seperate memory to both the internal tables.
Hi Puneet,
Thanks for the feedback.
I’ve changed the release_memory method and the result was exactly the same but with better performance.
New RELEASE_MEMORY method.
METHOD release_memory.
FIELD-SYMBOLS: <fs_table> TYPE STANDARD TABLE,
<fs_line> TYPE ANY,
<fs_tab_line> TYPE ANY.
DATA: lo_struct_typ TYPE REF TO cl_abap_structdescr,
lo_dyntable_typ TYPE REF TO cl_abap_tabledescr,
lt_dyntable TYPE REF TO data,
ls_dyntable TYPE REF TO data.
READ TABLE ch_t_tab INDEX 1
ASSIGNING <fs_tab_line>.
lo_struct_typ ?= cl_abap_typedescr=>describe_by_data( <fs_tab_line> ).
lo_dyntable_typ = cl_abap_tabledescr=>create( p_line_type = lo_struct_typ ).
CREATE DATA: lt_dyntable TYPE HANDLE lo_dyntable_typ.
ASSIGN: lt_dyntable->* TO <fs_table>.
<fs_table> = ch_t_tab.
APPEND INITIAL LINE TO <fs_table>.
DELETE <fs_table> INDEX sy–tabix.
FREE ch_t_tab.
ch_t_tab[] = <fs_table>.
FREE <fs_table>.
ENDMETHOD. “release_memory
Thanks!
Best regards,
Christian
Hi Randolf,
I’ve found the documentation of the Garbage Collector, that says:
Reference: http://help.sap.com/saphelp_glossary/en/35/26b214afab52b9e10000009b38f974/content.htm
As you said, garbage collector has nothing to do with the memory allocation of internal tables.
Thanks,
Christian
Hi Christian,
Great explanation.
I am trying in my system and I am not getting correctly what you explained. After delete also it is having almost same memory i.e. Minor change in the memory. Do you miss any thing in the code part..
BR,
Bharani
Hi SeethaRamaiah,
Thanks for your feedback.
Sorry but I didn’t understand your question. Do you mean that after the method call the memory used still the same?
Regards,
Christian
Yes Christian, Memory is increased after doing your method…
BR,
Bharani.
Can you paste your code here or send it to me?