Frans Whyburd wrote:
Does the following code have a memory leak ?

type
  rRecord = record
    i : integer;
    str : string;
  end;

  pRecord = ^rRecord;


procedure TForm1.testForLeak;
var
  i : integer;
  s : string;
  pnt : pRecord;
  list : Tlist;

begin
  list := Tlist.create;
  for i := 0 to 1000000 do
    begin
    new(pnt);
    pnt.l1 := 1;
    pnt.l2 := 'a';

Error: Undeclared identifier: 'l1'
Error: Undeclared identifier: 'l2'

*Always* copy and paste your *real* code.

    list.Add(pnt);
    end;

  for i := 0 to list.Count -1 do
    begin
    dispose(list.Items[i]);
    end;

Yes, this code has a memory leak. The Dispose procedure is a compiler-magic routine. It knows how much memory to free, and how to free it, based on the type of the pointer you pass to it. Likewise with the New procedure.

You pass a PRecord to New. It allocates SizeOf(TRecord) bytes, and then it initializes the str field of the record to ensure it holds a valid string value. (If you use GetMem to allocate the record, the str field remain uninitialized, and using it will probably result in an access violation. That's why we have the Initialize procedure.)

The pointer you pass to Dispose is just of type Pointer, not PRecord. That's because Pointer is the declared type of the TList.Items property. Therefore, Dispose doesn't know that there is a str field, so it doesn't do anything about freeing the string's memory. So you're leaking at least 10 bytes per record (the number of bytes in the string data), and probably more, based on the allocation granularity of the memory manager.

To fix the problem, do this:

for i := 0 to Pred(list.Count) do begin
  pnt := list[i];
  Dispose(pnt);
end;

It isn't necessary to call Clear before you free the TList; the list's destructor does that for you already.


Actually, in the particular code you showed, there *isn't* a memory leak. That's because the 'a' string literal is stored as a constant in your program's data segment -- it doesn't get allocated from the heap. Each instance of the str field holds a reference to the same string. Even if you called Dispose properly, the memory for those strings would never get freed because the memory manager never allocated them.

In general, though, I assume those strings won't all come from constants, and so you will eventually leak memory.

--
Rob

__________________________________________________
Delphi-Talk mailing list -> [email protected]
http://www.elists.org/mailman/listinfo/delphi-talk

Reply via email to