ABAP Geek 16 – Staying Alive
This one might be interesting for you, if you search for memory leaks.
Instances of classes or simply “objects” are created by CREATE OBJECT. Anonymous data objects are created by CREATE DATA. After creation, references (object references and data references) contained in reference variables are pointing to these objects. We all know that the objects are kept alive by these references and that they are deleted by the garbage collector if they are not referenced any more. But what kind of references are keeping objects alive? Let’s have look.
We start with the memory where objects are stored. In the working memory of an ABAP program (the well known roll area of an internal session) we can distinguish two further memory areas:
- The heap – the heap is the memory where all objects created by the CREATE statements, i.e. instances of classes and anonymous data objects, are stored.
- The stack – the stack is the memory where the (named) data objects of programs and procedures are stacked. The memory occupied by the local data of a procedure (including methods) is popped from the stack, when the execution of the procedure has finished.
Now it is easy to distinguish two types of reference variables:
- Heap references -heap references point to objects and parts of objects in the heap. Heap references are created when new objects are created with CREATE OBJECT or CREATE DATA. But you can also create heap references using the statement GET REFERENCE or the addition REFERENCE INTO for data objects or part of data objects in the heap.
- Stack references – stack references. point to objects and parts of objects in the stack. Stack references can be created only using the statement GET REFERENCE or the addition REFERENCE INTO for data objects or part of data objects in the stack.
An object reference is always pointing to an instance of a class in the heap and therefore an object reference is always a heap reference.
A data reference is a heap reference, if it points to an instance attribute or a part of an instance attribut of an object in the heap, or if it points to an anonymous data object or a part of an anonymous data object in the heap. A data reference is a stack reference, if it points to a data object or a part of a data object on the stack. A part of an instance attribute or of a data object can be a component of a structure, a line of an internal table, or a section specified by an offset and length.
And now about keeping objects alive:
- Heap references pointing to objects or parts of objects in the heap keep the objects alive.
- Stack references pointing to (data) objects or parts of (data) objects in the stack do not keep the objects alive.
With other words and for example: A data reference pointing to a line of an internal table that is an instance attribute of an object keeps the object alive, even if you have cleared the object reference variable!
CLASS cls DEFINITION.
PUBLIC SECTION.
DATA itab TYPE TABLE OF i.
METHODS constructor.
ENDCLASS.
CLASS cls IMPLEMENTATION.
METHOD constructor.
APPEND 1 TO itab.
ENDMETHOD.
ENDCLASS.
DATA: oref TYPE REF TO cls,
dref TYPE REF TO data.
START-OF-SELECTION.
CREATE OBJECT oref.
READ TABLE oref->itab INDEX 1 REFERENCE INTO dref.
CLEAR oref.
WAIT UP TO 1 SECONDS.
IF dref IS BOUND.
MESSAGE ‘Alive!’ TYPE ‘I’.
ENDIF.
And even more: Field symbols pointing to objects or part of objects in the heap (if you use the statement ASSIGN or the addition ASSIGNING for heap objects or part of heap objects) also keep the objects alive!
Last but not least a word about validity: Since heap references keep objects alive, as a rule they are always valid (IS BOUND is true). But ABAP wouldn’t be ABAP if there were not one exception from that rule: Internal tables are dynamic data objects with an own memory management that is independent from CREATE and garbage collection. A data reference pointing to a line of an internal table in the heap is a heap reference and keeps the table and its surrounding object alive. Nevertheless, it can become invalid (IS BOUND is false) if the internal table line is deleted from the table. For strings you could expect the same, but ABAP does not allow you to point to sections of strings with references or field symbols. Stack references can always become invalid of course, if the referenced objects are popped from the stack.
thanks for sharing this. Would like to see more of such deep ABAP topics from you.
Kind regards,
Hermann
this is the first time I read such a good explanation of heap and stack concepts within ABAP and ABAP objects.
Yes, please, more like this!
Regards,
Clemens
Hello Horst,
There is a very interesting question on StackOverflow where the user asks whether the string representing an object reference like the one you see in the debugger, e.g. "{O:9*\PROGRAM=ZAVG_DELETE_THIS\CLASS=LCL_SMTH}", can be used to obtain an actual reference.
Firstly, because the debugger is able to do this, and secondly because it seems the number after the "O:" points to a reference on the heap, I assume this must be possible.
(As indicated in the question, the user is satisfied with having a variable name reference to the instance, but it is nonetheless a very interesting question).
Regards,
Martin
Thanks Horst for your explanation!
But unfortunately ABAP cannot handle freed data properly. For example this code produce dump SYSTEM_DATA_ALREADY_FREE:
After adding the first reference of local variable to the global table with key, when form start executed for the second time the first reference is freed and the key of table become illegal.
The programmer cannot do anything in this situation and the dump occurs.
If working with table without using the key everything is fine.
I don't know workarounds except deleting lines with freed data before reading the table.
Can anybody help me?
Iurii,
But that concerns the last sentence of the blog,
"Stack references can always become invalid of course, if the referenced objects are popped from the stack."
The reference is not freed (no Garbage Collection), but it is a stack reference that pointed to lv_data during the execution of the procedure. In the moment the procedure is left, it is popped from the stack and the reference in the table line becomes invalid.
This is rather a programming error than a problem of the language.
A simplified form of the same "programming error":
Best
Horst
Horst, I understand you, but disagree that it is a "programming error".
I mean that I don't want access lines of table with illegal references but cannot do it fast using a key of a table.
For example, this code don't work:
I even cannot read this table using a key if there is a line with "FREED STACK:{A:1*\TYPE=%_T00006S00000000O0000000291}":
I can only delete illegal lines this way and read table using a key:
But it isn't fast. I have to loop at every row and if the table is big it is time-consuming.
Hi Iurii,
First,
Well, yes. As explained in the respective documentation, the key access cannot be optimized and therefore, using USING KEY is not possible. Without USING KEY it works.
Second,
I still can’t see the rationale of putting references to local data objects into a global internal table. Would be the same for putting references to local data objects of a method into an internal table that is an attribute of the class. It is clear that such a reference becomes invalid when leaving the procedure, why storing it in a non-local context? Maybe you want to use it in a procedure called from the local context? Then it would be better to tear down the full table when leaving the context.
Third,
I don’t see your use case but replacing
with
in your program above prevents the exception.
Horst
Thank you, Horst!
Yes, the documentation is almost perfect. I really love it and you are doing a great work.
But I cannot use index when there is NOT clause and this code works slowly:
Yes, I already use this workaround using STATICS but it is not "pretty" and consumes more memory.
Let my try to explain what actually I am doing. All variables are local:
I need to know if the selection was done using the table lt_flight or not. There is a static table in a class which collects references of it_key. And when it_key is popped from the stack this static table becomes invalid. I don't know was it_key local or not.
The only way to workaround this problem I see that you suggested me:
But it is not comfortable to create local classes in every method/procedure. I wanted to use static methods...
I see your point now.
The question is, why do the internal table statements try to access the unbound/invalid reference variables instead of treating them like bound ones.
Other examples:
Interesting enough, I can read an invalid or insert an invalid if no sorting or key access is involved.
I will ask the internal table guys about that restriction.
Yes, Horst! It is the case! Thank you a lot! 🙂
Iurii,
There was in fact a gap in the documentation. Therefore, I answer in a longer blog.
Unfortunately, the answer won't make you happy.
Horst
It is what it is. Thank you for investigation 🙂