Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2009-10

How to free this memory and avoid memory leak

13 messages · 6 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Graeme Geldenhuys

Hi,

I enabled heaptrc and it reported on block of memory not being freed. I tracked it down to the following method in my code. I have tried everything I can think of, but the memory leak still persists. Could anybody help me please.

A quick description of what this code does. I'm reading content out of a file. The content is an array of offsets into the file where data is located.

* ntoc = number of entries in the array. Each entry is a LongWord (or Int32 in the code below)
* tocarray is my local array that gets populated with information from the file, using the Move() procedure.
* tocoffsetsstart is the starting offset of the TOC array in the file.
* I then simply look through the number of entries in the array
- position p to file offset as specified by each entry of the array
- extract my data, and continue with the next item in the array

At the end I try to free / dispose of the memory used by my local array: tocarray
...but whatever I try, heaptrc keeps telling me I have a memory leak in this code.

--------------------------------------
procedure THelpFile.ReadContents;
var
  Topic: TTopic;
  EntryIndex: longint;
  pEntry: pTTOCEntryStart;
  tocarray: array of Int32;
  p: PByte;
begin
  _Topics.Capacity := _Header.ntoc;
  SetLength(tocarray, _Header.ntoc);
  p := _Data + _Header.tocoffsetsstart;
  Move(p, tocarray, SizeOf(tocarray));
  for EntryIndex := 0 to _Header.ntoc-1 do
  begin
    pEntry := _Data + tocarray[EntryIndex];
    Topic := TTopic.Create(_Data,
                           _Header,
                           _Dictionary,
                           pEntry );
    Topic.HelpFile := Self;
    Topic.Index := EntryIndex;
    _Topics.Add(Topic);
  end;
//  Finalize(tocarray);                        <--- doesn't work
  Finalize(tocarray, _header.ntoc);       <--- doesn't work
  tocarray := nil;                                  <--- doesn't work
end;
--------------------------------------
Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://opensoft.homeip.net/fpgui/
Jonas Maebe
to Graeme Geldenhuys
Graeme Geldenhuys wrote 13 lines
procedure THelpFile.ReadContents; var Topic: TTopic; EntryIndex: longint; pEntry: pTTOCEntryStart; tocarray: array of Int32; p: PByte; begin _Topics.Capacity := _Header.ntoc; SetLength(tocarray, _Header.ntoc); p := _Data + _Header.tocoffsetsstart; Move(p, tocarray, SizeOf(tocarray));

This has to be

move(p^, tocarray^, length(tocarray)*sizeof(tocarray[0]));

Otherwise you are just moving the contents of "p" into tocarray, and the pointer to the memory allocated for tocarray is gone forever. You're lucky (or unlucky) the program didn't crash when finalising tocarray.

Also, as you can see the program does not really depend on the data being actually stored in a dynamic array. So you could just as well use "tocarray: ^Int32" and assign it the value of p (as you pretty much did until now).

Jonas

Vincent Snijders
to Graeme Geldenhuys: I enabled heaptrc and it reported on block of memory not being freed. I tracked it down…
Graeme Geldenhuys wrote 16 lines
Hi, -------------------------------------- procedure THelpFile.ReadContents; var Topic: TTopic; EntryIndex: longint; pEntry: pTTOCEntryStart; tocarray: array of Int32; p: PByte; begin _Topics.Capacity := _Header.ntoc; SetLength(tocarray, _Header.ntoc); p := _Data + _Header.tocoffsetsstart; Move(p, tocarray, SizeOf(tocarray));
Move(p, tocarray[0], SizeOf(tocarray));
Show quoted text 17 lines
for EntryIndex := 0 to _Header.ntoc-1 do begin pEntry := _Data + tocarray[EntryIndex]; Topic := TTopic.Create(_Data, _Header, _Dictionary, pEntry ); Topic.HelpFile := Self; Topic.Index := EntryIndex; _Topics.Add(Topic); end; // Finalize(tocarray); <--- doesn't work Finalize(tocarray, _header.ntoc); <--- doesn't work tocarray := nil; <--- doesn't work end; --------------------------------------
Graeme Geldenhuys
to Jonas Maebe: This has to be move(p^, tocarray^, length(tocarray)*sizeof(tocarray[0])); Otherwise you…

2009/10/8 Jonas Maebe :

Show quoted text 7 lines
>  SetLength(tocarray, _Header.ntoc); >  p := _Data + _Header.tocoffsetsstart; >  Move(p, tocarray, SizeOf(tocarray)); This has to be move(p^, tocarray^, length(tocarray)*sizeof(tocarray[0]));

^
This gives a compiler error. Illegal qualifier and points to the second ^ in that statement.

    move(p^, tocarray, _Header.ntoc*sizeof(tocarray[0]));

Causes a segmentation (p, tocarray[0], SizeOf(tocarray)); This causes an Access problem is not reading in the data. It reads in perfectly, it's just the cleanup of the local array that I am battling with. Heaptrc keeps telling me there is a memory leak.

Initially I tried to "overlay" a record structure over each offset into the file, to simply read the data via the record structure. No need for the local array, but for the life of me, I couldn't get that to work. I kept reading corrupt data.

Otherwise you are just moving the contents of "p" into tocarray, and the pointer to the memory allocated for tocarray is gone forever. You're lucky (or unlucky) the program didn't crash when finalising tocarray.

When I included the finalize() call, it does cause a crash. :-(

Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://opensoft.homeip.net/fpgui/
Andrew Brunner
to Graeme Geldenhuys
     Move(p, tocarray[0], SizeOf(tocarray)); This causes an Access Violation at runtime.

This should not cause as RAV. You must call
SetLength(toarray,SizeOfMemory) and also don't use SizeOf(tocarray) use Length(tocarray)*SizeOf(What ever the element is))

Graeme Geldenhuys
to Jonas Maebe: This has to be move(p^, tocarray^, length(tocarray)*sizeof(tocarray[0])); Otherwise you…

2009/10/8 Jonas Maebe :

Also, as you can see the program does not really depend on the data being actually stored in a dynamic array. So you could just as well use "tocarray: ^Int32" and assign it the value of p (as you pretty much did until now).

Ah! That is what I wanted to do in the first place, but I obviously I didn't get the ^int32 syntax correct.

It now works perfectly, without the need of the local array. :-) And more importantly, no more memory leak. ;-)

Thanks Jonas.

Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://opensoft.homeip.net/fpgui/
Vincent Snijders
to Graeme Geldenhuys: 2009/10/8 Jonas Maebe : ^ This gives a compiler error. Illegal qualifier and points to the
Graeme Geldenhuys wrote 19 lines
2009/10/8 Jonas Maebe : >> SetLength(tocarray, _Header.ntoc); >> p := _Data + _Header.tocoffsetsstart; >> Move(p, tocarray, SizeOf(tocarray)); > This has to be > > move(p^, tocarray^, length(tocarray)*sizeof(tocarray[0])); ^ This gives a compiler error. Illegal qualifier and points to the second ^ in that statement. move(p^, tocarray, _Header.ntoc*sizeof(tocarray[0])); Causes a segmentation fault at runtime. Move(p, tocarray[0], SizeOf(tocarray)); This causes an Access Violation at runtime.

Move the data pointed at by p into the array elements without overwriting the dynarray meta data structure.

Move(p^, tocarray[0], sizeof(Int32)*_Header.ntoc);

Working with pointers, if you don't get it, maybe just use a loop.

Vincent

Mattias Gärtner
to Graeme Geldenhuys: I enabled heaptrc and it reported on block of memory not being freed. I tracked it down…

Zitat von Graeme Geldenhuys :

Show quoted text 29 lines
[...] * ntoc = number of entries in the array. Each entry is a LongWord (or Int32 in the code below) * tocarray is my local array that gets populated with information from the file, using the Move() procedure. * tocoffsetsstart is the starting offset of the TOC array in the file. * I then simply look through the number of entries in the array - position p to file offset as specified by each entry of the array - extract my data, and continue with the next item in the array At the end I try to free / dispose of the memory used by my local array: tocarray ...but whatever I try, heaptrc keeps telling me I have a memory leak in this code. -------------------------------------- procedure THelpFile.ReadContents; var Topic: TTopic; EntryIndex: longint; pEntry: pTTOCEntryStart; tocarray: array of Int32; p: PByte; begin _Topics.Capacity := _Header.ntoc; SetLength(tocarray, _Header.ntoc); p := _Data + _Header.tocoffsetsstart; Move(p, tocarray, SizeOf(tocarray));
if _Header.ntoc>0 then
   Move(p^,tocarray[0],SizeOf(Int32)*_Header.ntoc);
for EntryIndex := 0 to _Header.ntoc-1 do begin pEntry := _Data + tocarray[EntryIndex];
Why not simply:
   pEntry := _Data + PInt32(p)[EntryIndex];
Show quoted text 9 lines
Topic := TTopic.Create(_Data, _Header, _Dictionary, pEntry ); Topic.HelpFile := Self; Topic.Index := EntryIndex; _Topics.Add(Topic); end; // Finalize(tocarray); <--- doesn't work

not needed:

Finalize(tocarray, _header.ntoc); <--- doesn't work tocarray := nil; <--- doesn't work end;

Mattias

Jonas Maebe
to Graeme Geldenhuys: 2009/10/8 Jonas Maebe : ^ This gives a compiler error. Illegal qualifier and points to the
Graeme Geldenhuys wrote 11 lines
2009/10/8 Jonas Maebe : >> SetLength(tocarray, _Header.ntoc); >> p := _Data + _Header.tocoffsetsstart; >> Move(p, tocarray, SizeOf(tocarray)); > > This has to be > > move(p^, tocarray^, length(tocarray)*sizeof(tocarray[0])); ^ This gives a compiler error. Illegal qualifier and points to the second ^ in that statement.
It should have read move(p^, tocarray[0],
length(tocarray)*sizeof(tocarray[0]));
move(p^, tocarray, _Header.ntoc*sizeof(tocarray[0])); Causes a segmentation 's is because you are blasting "_Header.ntoc*sizeof(tocarray[0])"

bytes of data onto the stack starting at the address of tocarray. sizeof(tocarray) = sizeof(pointer). You are therefore overwriting everything on the stack coming after that variable (= higher on the stack).

Move(p, tocarray[0], SizeOf(tocarray)); This causes an Access 's because you are only initialising the first element (with the

*value* of p no less, which is not a valid index in your file, but a pointer to some memory location), and later on reading every other element and using it as index in your loop.

My problem is not reading in the data. It reads in perfectly,

I explained in my original mail what that is the case (which you apparently saw in the mean time).

it's just the cleanup of the local array that I am battling with.

Also with understanding how dynamic arrays and/or move work. Dynamic arrays are reference counted pointers to data blobs. sizeof(dynamic_array_var) = sizeof(pointer), always, and regardless of the length of the array. It's like sizeof(class) = sizeof(ansistring) = sizeof(pointer). Move() takes "var" parameters as first and second parameters, which means that it will read data from the memory *occupied by* the first argument (rather than memory it may point at) and store data into the memory *occupied by* the second argument (idem).

Heaptrc keeps telling me there is a memory leak.

It is correct.

Jonas

Graeme Geldenhuys
to Vincent Snijders: Move the data pointed at by p into the array elements without overwriting the dynarray…

2009/10/8 Vincent Snijders :

Working with pointers, if you don't get it, maybe just use a loop.

I have to admit. After working for so long with RDBMS API's, I got quite rusty with the more low level file reading, record structures and pointer arithmetic. Database desktop applications clearly makes one lazy! :-)

Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://opensoft.homeip.net/fpgui/
Graeme Geldenhuys
to Jonas Maebe: It should have read move(p^, tocarray[0], length(tocarray)*sizeof(tocarray[0])); bytes of…

2009/10/8 Jonas Maebe :

Show quoted text 9 lines
Also with understanding how dynamic arrays and/or move work. Dynamic arrays are reference counted pointers to data blobs. sizeof(dynamic_array_var) = sizeof(pointer), always, and regardless of the length of the array. It's like sizeof(class) = sizeof(ansistring) = sizeof(pointer). Move() takes "var" parameters as first and second parameters, which means that it will read data from the memory *occupied by* the first argument (rather than memory it may point at) and store data into the memory *occupied by* the second argument (idem).

Thanks for that explanation, it makes a lot more sense now. I also didn't know this:
sizeof(dynamic_array_var) = sizeof(pointer)

I wouldn't be making that mistake soon. I better get my "annotation" support fully working in the new INF help viewer, so I can add my own notes to the Language Reference docs. :)

> Heaptrc > keeps telling me there is a memory leak. It is correct.

I never doubted heaptrc. :)

Regards, - Graeme - _______________________________________________ fpGUI - a cross-platform Free Pascal GUI toolkit http://opensoft.homeip.net/fpgui/