Understanding managed types and Pointers lazarus / 62-General ---------------------------------------------------------------------- kupferstecher · 2022-03-11 19:07 Hello, currently I'm trying to understand how reference counting and the managed types work in connection with pointers. I searched a lot about this topic, but I didn't find any information regarding to pointers on managed types. Please see the below test application. In the "main" program a byte array is created and a record structure as pointer applied to the bytearray. The record contains a string type. When assigning a value to the string, the compiler automatically reserves the proper memory on the heap, where the string content and ref count is stored. So far so clear. I believe, that the compiler inserts some code at the end of the procedure, to free the strings heap data (FINALIZE?), because the overlaying managed type goes out of scope. Though I'm not sure, how the pointer influences this behaviour. Then when I pass the array to an other procedure and override the array data, I'm getting more confused. If I use fillbyte (Case C) and write the values to zero, then a memory leak occurs. I can understand that, as the compiler tries to clean up the string later, reads out the value "nil", so recognizes the string as already empty. In case D I don't write Zeros to the array but a nonzero value. Then a segmentation fault occurs. I also can understand that, because the cleanup code interpretes the value as pointer to the string data, but its not a valid address, so... But Case A (freeing the array) and B (overriding in a loop) is strange. In both cases the code runs through smoothly and the heap trace doesn't report any memory leak. So how does the compiler recognize that the record pointer is not valid anymore after calling the Manipulate procedure? Is this a compile-time decission, resulting in the array clean up code just beeing ommited? Or does something happen on runtime? I think there is no copy operation of the data array, as trying to read out the string fails after calling the Manipulate procedure. Then the question arises, is case A (freeing the array) reliable in regard to the string beeing properly freed? Thanks! Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program project1; {$mode ObjFPC}{$H+} uses Classes, SysUtils; Type TByteData = array of byte; Type TMyRec = packed record    p1: Int32;    p2: Ansistring;    //p3: array of uInt32;end; Procedure Manipulate(var aData:TByteData);var  pMyRec: ^TMyRec;  ii: Integer;begin   pMyRec:= @aData[0];  writeln('Rec.p2:',pMyRec^.p2); //  setlength(aData,0);          //A: HeapTrc: No mem leak //  for ii:= 0 to high(aData)//  do aData[ii]:= 0;              //B: Heaptrc: No mem leak //  fillbyte(aData,length(aData),0); //C: Heaptrc: Mem leak (string not freed)  fillbyte(aData,length(aData),1); //D: Segmentation Fault when closing program end; var  data: TByteData;  pMRec: ^TMyRec;  begin  Se tHeapTraceOutput('trace.log'); setlength(data,SizeOf(TMyRec));pMrec:= @data[0]; pMRec^.p2:= 'eins'; Manipulate(data); writeln('Program finished.');READLN; end. ---------------------------------------------------------------------- Jonas Maebe · 2022-03-12 18:13 The reason the unfreed string does not cause memory leaks, is that constant strings are statically allocated by the compiler in the program's constant data section (with a reference count of -1), rather than on the heap. A dynamic array, however, is also a reference counted type that is implicitly a pointer, like ansistrings. In cases C and and D, you are filling the dynamic array pointer itself with 0 resp. 1, and the memory leak/crash happens because the array itself does not get freed resp. contains an invalid pointer. In cases A and B, you are freeing the array (which also sets it to a nil pointer) resp. filling its contents with 0. Hence, the array itself gets freed normally in both cases (inside Manipulate in case A and in the main program in case B) and there is no memory leak ---------------------------------------------------------------------- kupferstecher · 2022-03-12 20:41 Thanks for the explanations! Quote from: Jonas Maebe on March 12, 2022, 07:13:23 pmThe reason the unfreed string does not cause memory leaks, is that constant strings are statically allocated by the compiler in the program's constant data section (with a reference count of -1), rather than on the heap.OK, so all the time I didn't test what I wanted to test... Now I "randomly" added a pointer address to the string assignment to be sure that it is a runtime string and allocated on the heap.   pMRec^.p2:= 'eins'+IntToHex(PtrUInt(@data)); Then A and B both produce a memory leak, which seems right. But if I remove the "Manipulate" procedure, i.e. just apply the string to the array then the memory leak is still there. -> Does it mean a string assigned through a pointer is never cleaned up?? This would clarify a lot. QuoteIn cases C and and D, you are filling the dynamic array pointer itselfI see, again, not what I wanted to achive So I changed this to   fillbyte(aData[0],length(aData),0); This should be the correct way to clear the array and not the heap data, then it behaves like the modified A and B (memory leak), seems correct. ---------------------------------------------------------------------- Warfley · 2022-03-12 22:02 Quote from: kupferstecher on March 12, 2022, 09:41:13 pmBut if I remove the "Manipulate" procedure, i.e. just apply the string to the array then the memory leak is still there. -> Does it mean a string assigned through a pointer is never cleaned up?? This would clarify a lot. Yes, the compiler does only initialize and cleanup directly declared variables, as well as new and dispose calls. When handling other forms of memory, use Initialize and Finalize Intrinsics: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  PTest=^TTest;  TTest = record    A: String;  end; var  Buff: Array[0..1024] of Byte;begin  Initialize(PTest(@Buff)^);  try    PTest(@Buff)^.A := 'Test';  finally       Finalize(PTest(@Buff)^);  end;end. ---------------------------------------------------------------------- kupferstecher · 2022-03-13 15:53 OK, thank you, also for the code! It's much clearer now. ---------------------------------------------------------------------- Thaddy · 2022-03-13 18:27 @Warfley And programmers would use 0..1023, not 1024.... - alignment - power of two, which comes in handy with many operations ---------------------------------------------------------------------- Warfley · 2022-03-16 09:16 I know, I just like 1024 as a number more, 1023 looks so much sader. But you are right, in productive code it would have been better to just start the array at 1 rather than 0