Question

After replacing hard type casts of AnsiString to TBytes (array of string) with a ToBytes method (see below), Delphi reported no memory leaks - Free Pascal 2.6.2 however shows a leak in case the TBytes value is passed to a method with a parameter of type Pointer.

The following code leaks memory:

program project1;

{$mode delphi}

uses
  SysUtils;

function ToBytes(const AValue: AnsiString): TBytes;
begin
  SetLength(Result, Length(AValue)); // <-- leak (ine 10)
  if Length(AValue) > 0 then
    Move(AValue[1], Result[0], Length(AValue));
end;

procedure Send(P: Pointer);
begin

end;

begin
  Send(ToBytes('test'));

  SetHeapTraceOutput('heaptrace.log');
end. 

Memory leak report:

Call trace for block $001C5CC0 size 12   $00401586  TOBYTES,  line 10
of project1.lpr   $00401622  main,  line 21 of project1.lpr

If I change the Send method to take an argument of type TBytes, the memory leak disappears.

Was it helpful?

Solution

That's a compiler bug. The managed type TBytes has reference counted lifetime. The compiler should create an implicit local variable which is assigned the array returned by ToBytes. You'll need to work around that by storing to an explicit local:

var
  Tmp: TBytes;
....
Tmp := ToBytes(...);
Send(Tmp);

OTHER TIPS

It is probably not a bug where you think it is. The heap tracing of FPC has known issues with tracking temps (and automated types in general) in main programs (the main .dpr begin..end).

Move the code to a procedure, and call that from the main begin..end. and you'll see the leak disappears.

This because the general structure of the main program is like

begin 
  initializeunits(); // procedure call inserted by the compiler
    <actual mainprogram statements>
  finalizeunits();   // procedure call inserted by the compiler
end.

with the releasing of mainprogram temps happening at "end." after finalizeunits that finalizes the heaptracking. (even if it is first unit, it is still only an unit). So heaptrc misses that.

Functions that return things that potentially comprise a lot of memory make me squeamish. This may be just a feeling or personal preference, but it does take away control over the allocation of the memory. There are similar things said about function that return (freshly constructed) instances of TStringList, there too it's advisable to pass a pointer to a TStrings object and let the caller keep control over the life-time of the object.

Here I would advise to use a var parameter for all operations on TBytes, and force the caller to provide an instance to work on. If you build an elaborate application and will have to search where you can improve performance, think about this again and see if you can recycle TBytes instances of similar content or length. An important feature of the Delphi string system is that is doing reference counting and copy-on-write for you behind the scenes, providing this performance gain when your application is handling a lot of (similar) strings.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top