Solved

Memory leak - how to get rid of custom created object

Posted on 2004-10-11
9
234 Views
Last Modified: 2011-09-20
Well, this has already cost me too many hours so I decided to sign up with experts-exchange, I hope you can help me here.

I'm not really a programming crack, so I just might be doing something really stupid but here is what I tried to do:

The object is part of a larger program, it is defined in one unit(say unit A) as follows:

TLattice = class(TObject)
  cell : array of array of TCell;
  size_dim1, size_dim2, lifetime : Integer;
  cellCountH, cellCountA1, cellCountA2, cellCountA3, cellCountD : array of integer;
  active_drugs : integer;
  drugs_in_system : array of TDrug;
  constructor create(Dimensie1, Dimensie2 : Integer);
end;

TCell and TDrug are defined as follows:

TCell = class(TObject)
  status : cell_status;
  nA1, nA2 : integer;
  strand: integer;
  to_update : cell_status;
  lifetime: integer;
  resist: integer;
  activated: bool;
  constructor create();
end;

TDrug = class(TObject)
  name: string;
  N: integer;
  Presp : double;
  lifetime: integer;
  constructor create();
end;

And, to be as complete as possible, the constructor for TLattice is this:

constructor TLattice.create(Dimensie1, Dimensie2 : Integer);
var
  Temp_Cell : TCell;
  Teller1, Teller2 : Integer;
begin

    setlength(cell,Dimensie1+(2*bufferzone),Dimensie2+(2*bufferzone));  // set dynamic array => dimensions + bufferzone
    self.size_dim1 := Dimensie1;
    self.size_dim2 := Dimensie2;

    setlength(cellCountH,1000);
    setlength(cellCountA1,1000);
    setlength(cellCountA2,1000);
    setlength(cellCountA3,1000);
    setlength(cellCountD,1000);

    for Teller1 := 0 to Dimensie1 + (2*bufferzone -1) do
    begin
      for Teller2 := 0 to Dimensie2 + (2*bufferzone -1) do  // vul lattice 2e dimensie
      begin
        Temp_Cell := TCell.create();
        self.cell[Teller1,Teller2] := Temp_Cell;
      end;
    end;
end;

Now, in one unit(unit A) I have registered a variable called Lattice as a global variable(in that unit)

var
   Lattice : TLattice;


From another unit(unit B) I call a wrapper procedure which is as follows:

procedure wrapper(some variables..)
begin
   Lattice := TLattice.create(700,700);

   Do some things with the Lattice..
   
   Then, in the end, I try to get rid of this object since I call this wrapper procedure many times and it causes gigantic memory leaks
   First I simply tried Lattice.free(); but this doesn't seem to work(I get a memory access error)
   (I don't access the Lattice anymore after this call)
   Next I tried to make a custom procedure(freeLattice) to clean it up and though it partly works(much less memory leak) it still doesn't totally solve  
   my problem
end;

procedure freeLattice(Lattice : TLattice);
var
  Teller, Teller1, Teller2, Dimensie1, Dimensie2: Integer;
begin

  Dimensie1 := Lattice.size_dim1;
  Dimensie2 := Lattice.size_dim2;

  for Teller1 := bufferzone to Dimensie1 + (bufferzone -1) do
  begin     for Teller2 := bufferzone to Dimensie2 + (bufferzone -1) do  
    begin
      Lattice.cell[Teller1,Teller2].Free();
    end;
  end;

  setlength(Lattice.cell,0,0);

  setlength(Lattice.cellCountH,0);
  setlength(Lattice.cellCountA1,0);
  setlength(Lattice.cellCountA2,0);
  setlength(Lattice.cellCountA3,0);
  setlength(Lattice.cellCountD,0);

  setlength(Lattice.drugs_in_system,0);
 
end;

I hope someone can shed some light on this! :)
0
Comment
Question by:reynaerde
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 3
9 Comments
 
LVL 7

Expert Comment

by:LRHGuy
ID: 12277883
Dynamic arrays are freed when their refernece count goes to zero, so you shouldn't have to any zeroing of them.

You probably need to release the drugs array, too...

TLattice=class
  destructor Destroy;
    override;
end;

Destructor TLattice.Destroy;

create(Dimensie1, Dimensie2 : Integer);
var
  Teller1, Teller2 : Integer;
begin

    for Teller1 := 0 to Size_Dim1 + (2*bufferzone -1) do
    begin
      for Teller2 := 0 to Size_Dim2 + (2*bufferzone -1) do
      begin
        self.cell[Teller1,Teller2].free;
      end;
    end;

  //Free Drugs_In_System array here...
  for x:=? to ? do
   Drugs_in_System[x].free;



  inherited destroy;
end;
0
 
LVL 7

Accepted Solution

by:
LRHGuy earned 450 total points
ID: 12277951
Let me try that again:

TLattice=class
  destructor Destroy;
    override;
end;

Destructor TLattice.Destroy;
var
  Teller1, Teller2 : Integer;
begin
    for Teller1 := 0 to Size_Dim1 + (2*bufferzone -1) do
    begin
      for Teller2 := 0 to Size_Dim2 + (2*bufferzone -1) do
      begin
        self.cell[Teller1,Teller2].free;
      end;
    end;

  //Free Drugs_In_System array here...
  for x:=? to ? do
   Drugs_in_System[x].free;

  inherited destroy;
end;

0
 

Author Comment

by:reynaerde
ID: 12278170
Well, unfortunately this doesn't change anything (other than perhaps a few bits less, the Drugs_in_system array is not used in my program yet)
Also, I find it strange that I get an error when I insert Lattice.free();
The exact error reads:

project raised exception class EAccessViolation with message 'Access violation at address blabla in module project_name. Read of address blabla
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 17

Assisted Solution

by:Wim ten Brink
Wim ten Brink earned 50 total points
ID: 12278360
> setlength(cell,Dimensie1+(2*bufferzone),Dimensie2+(2*bufferzone));  // set dynamic array => dimensions + bufferzone

And this code works??? AFAIK, you can only use 2 parameters, not 3... If you have a two-dimensional array then you first set the number of items in your first dimension, then set the number of items for every item in the second dimension. Something like this:

var I:Integer;
begin
  setlength(cell, Dimensie1+(2*bufferzone));
  for I := Low(cell) to High(cell) do setlength(cell[I], Dimensie2+(2*bufferzone));
...blablabla...

And if you use dynamic arrays, start relying on the methods Low(), High() and Length() since these are real friends to you in those cases.

Now, also add a "destructor Destroy; override;" line to your TLattice object. This should free whatever you've created in the Create method. Don't free that stuff from outside your object! The same would be true for TCell and TDrug if they would allocate memory for other components but they don't seem to do this.

Keep in mind that dynamic arrays know their own sizes. You don't have to keep track of this yourself. Low() is the first element and with dynamic arrays always equals 0. High() is the last element and always equals Length()-1 for dynamic arrays. If you want to loop through a dynamic array, just go from Low() to High() or in the other direction, since this is always safe. (Unless you assigned invalid data.) The Length() just tells you the number of elements.
And a two-dimensional dynamic array doesn't exist in Delphi. What you get is an array of arrays and theoretically, each row could have a different number of columns.

And "setlength(Lattice.cell,0,0);" isn't even supposed to compile... Did you create your own version of this routine?
0
 
LVL 7

Expert Comment

by:LRHGuy
ID: 12278427
From the DELPHI 7 help:

To declare multidimensional dynamic arrays, use iterated array of ... constructions. For example,

type TMessageGrid = array of array of string;
var Msgs: TMessageGrid;

declares a two-dimensional array of strings. To instantiate this array, call SetLength with two integer arguments. For example, if I and J are integer-valued variables,

SetLength(Msgs,I,J);

allocates an I-by-J array, and Msgs[0,0] denotes an element of that array.
0
 

Author Comment

by:reynaerde
ID: 12278457
Actually, I just got it from the Delphi help(using Delphi 7)

'To declare multidimensional dynamic arrays, use iterated array of ... constructions. For example,

type TMessageGrid = array of array of string;
var Msgs: TMessageGrid;

declares a two-dimensional array of strings. To instantiate this array, call SetLength with two integer arguments. For example, if I and J are integer-valued variables,

SetLength(Msgs,I,J);

allocates an I-by-J array, and Msgs[0,0] denotes an element of that array.'

I know the 2-dimensional array consists of arrays within arrays, but that suits my purpose fine.

Thanks for the comments about the low(), high() and length() methods though.
0
 
LVL 7

Expert Comment

by:LRHGuy
ID: 12278498
It might be a problem with the USAGE of lattice. I tried all the code that's been posted and don't get any errors. Not even a memory leak. I do not have the create/destroy methods for tcell though.

It's possible something your doing with lattice is overwriting memory or some other problem.
0
 
LVL 7

Expert Comment

by:LRHGuy
ID: 12278558
Just to clarify...I'm using your lattice.create, my lattice.destroy and doing this:

procedure TForm2.Button2Click(Sender: TObject);
begin
   Lattice := TLattice.create(700,700);
   lattice.free;
end;

With no error.

If you post more code (tcell.create) and some usage, someone may be able to shed some light on it.
0
 

Author Comment

by:reynaerde
ID: 12278858
Ah, yes! You are entirely right. As unfortunately happens so often I overlooked something quite important.
I was under the impression I was clearing the Lattice using

for Teller1 := bufferzone to Dimensie1 + (bufferzone -1) do
  begin
    for Teller2 := bufferzone to Dimensie2 + (bufferzone -1) do  
    begin
      Lattice.cell[Teller1,Teller2].Free();
    end;
  end;

But clearly I needed to get the whole array (0 to Size_Dim2 + (2*bufferzone -1))

Thanks a bunch for helping!
(and thanks to alex for giving me some pointers for using high, low etc)
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
A short tutorial showing how to set up an email signature in Outlook on the Web (previously known as OWA). For free email signatures designs, visit https://www.mail-signatures.com/articles/signature-templates/?sts=6651 If you want to manage em…

737 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question