Solved

Memory leak - how to get rid of custom created object

Posted on 2004-10-11
9
227 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
  • 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
 
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
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
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

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

706 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

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now