Go Premium for a chance to win a PS4. Enter to Win

x
?
Solved

Memory leak - how to get rid of custom created object

Posted on 2004-10-11
9
Medium Priority
?
238 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 1800 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
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 17

Assisted Solution

by:Wim ten Brink
Wim ten Brink earned 200 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

How to Use the Help Bell

Need to boost the visibility of your question for solutions? Use the Experts Exchange Help Bell to confirm priority levels and contact subject-matter experts for question attention.  Check out this how-to article for more information.

Question has a verified solution.

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

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Exchange organizations may use the Journaling Agent of the Transport Service to archive messages going through Exchange. However, if the Transport Service is integrated with some email content management application (such as an anti-spam), the admin…
In a question here at Experts Exchange (https://www.experts-exchange.com/questions/29062564/Adobe-acrobat-reader-DC.html), a member asked how to create a signature in Adobe Acrobat Reader DC (the free Reader product, not the paid, full Acrobat produ…
Suggested Courses

876 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