Freeing a record which contains a string field leads to a memory/string leak ?!

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by Skybuck Fl » Fri, 20 Feb 2004 19:25:24


i,

I am working with a double linked list, nodes, and data for the nodes. I
just wrote some new code. The new code uses a record which contains a delphi
string and a boolean.

The data is a record which is allocated with getmem. Then the string field
inside the data record is set/assigned. Later it is also read. Then finally
the data is destroyed with FreeMem.

Now my concern is with delphi's reference counting.

I know that delphi keeps track if a string still exists. I believe that my
described scenerio could lead to a memory leak because the string is
actually never destroyed. In the past I would create a class and then call
the destroy for the class that way I was sure that the strings would get
destroyed.

But now in my new code I would like to avoid having to use classess to wrap
the strings in (and thereby preventing memory leaks and other problems)...
So I ask this newsgroup if there is another solution to this problem ?

How can I force delphi to destroy a string ? ( Before I destroy the data
with freemem )

To illustrate my problem I have created some simple sample code. ( I am not
that good with a debugger so I am not sure if this code will lead to a
memory leak after DestroyData is called... maybe you can analyze/debug this
code for me and tell me if it really does lead to a memory/string leak and
how you figured that out :D )

Source and DFM are included:

( To use this code and dfm, create a new application, replace the unit1 code
with this code, then go to the form, view as text, then replace
the text with the dfm text )

*** Source Begins Here ***

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
Pdata = ^Tdata;
Tdata = record
mAddress : string;
mMembership : boolean;
end;

type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Edit1: TEdit;
CheckBox1: TCheckBox;
Edit2: TEdit;
CheckBox2: TCheckBox;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
Data : Pdata;

procedure CreateData;
procedure DestroyData;
procedure SetData( const Address : string; const Membership : boolean );
procedure GetData( var Address : string; var Membership : boolean );
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.CreateData;
begin
GetMem( Data, sizeof(Tdata) );
end;

procedure TForm1.DestroyData;
begin
FreeMem( Data, sizeof(Tdata) );
end;

procedure TForm1.SetData( const Address : string; const Membership :
boolean );
begin
Data.mAddress := Address;
Data.mMembership := Membership;
end;

procedure TForm1.GetData( var Address : string; var Membership : boolean );
begin
Address := Data.mAddress;
Membership := Data.mMembership;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
CreateData;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
SetData( Edit1.Text, CheckBox1.Checked );
end;

procedure TForm1.Button3Click(Sender: TObject);
var
s : string;
b : boolean;
begin
GetData( s, b );
Edit2.Text := s;
CheckBox2.Checked := b;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
De
 
 
 

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by Skybuck Fl » Fri, 20 Feb 2004 19:54:24

I think the code is ok...

Thanks to this article the reference count can be inspected.

http://www.yqcomputer.com/

Here is some new code which inspects it.

procedure TForm1.Button1Click(Sender: TObject);
begin
CreateData;
d1 := cardinal(Data); // storing pointer value when it was created
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
DestroyData;
d2 := cardinal(Data); // store pointer value after it was destroyed
end;

// inspect it.
procedure TForm1.Button5Click(Sender: TObject);
var
ReferenceCount : integer;
begin
if d1=d2 then ShowMessage('pointer still the same'); // is it still the
same address

ReferenceCount := integer( pointer( Cardinal(Data.mAddress) - ( 4 +
4 ) )^ );

ShowMessage( 'string reference count: ' + IntToStr(ReferenceCount) );
end;

The message will show zero after destroy data... so I think the reference
count of the string is zero ?

Though this is hard to believe for me because in the past I have had
problems with strings like this ?!

Maybe inspecting like this is flawed ?!

Well, I am keeping my fingers crossed ;)

If I do get problems in the future maybe just using mData.mAddress := '';
will solve it ;)

But then each node will have to be destroyed manually instead of the general
destroy code of the double linked list etc..

Oh well.

Bye,
Skybuck.

 
 
 

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by erewho » Fri, 20 Feb 2004 20:03:00

On Thu, 19 Feb 2004 11:25:24 +0100, "Skybuck Flying"


<snip>

The simple answer is Data.mAddress := '' ;

The real answer is ... more of a question

You talk about linked lists
- so you are going to have shedloads of these records

Why not use a Dynamic Array to hold them ?

All this GetMem and FreeMem stuff is not really necessary nowadays
with Delphi
- if anything it is rather dangerous.
 
 
 

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by erewho » Fri, 20 Feb 2004 21:14:29

On Thu, 19 Feb 2004 11:54:24 +0100, "Skybuck Flying"


<snip>



Especially if you put that in your (misnamed) Destroy routine


You might be a lot better off forgetting about linked lists
- just keep a sorted array of Integer pointers to the records

About 20 years ago I got enthusiatic about linked lists
- and then realized that I was being a fool
 
 
 

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by Skybuck Fl » Fri, 20 Feb 2004 23:33:55


Don't worry the double linked list is flawless. Been used a lot. if it aint
I will solve.

Now to your question why not use dynamic arrays.

That's a lot harder lol. Having to reallocate them when the grow etc.

And problems removing and inserting stuff etc.

The only valid thing you mentioned is the getmem and freemem... indeed doing
lot's of these can be a slowdown...
that however can be solved by a buffer pool where nodes are hung back when
they are no longer needed
delaying the need to create new structures until the buffer pool is empty.
 
 
 

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by Skybuck Fl » Fri, 20 Feb 2004 23:43:42


general

Why was that.. because you weren't able to program it ?

Hell maybe now you are a better programmer and you should try again.

I am sure you could do it if you really wanted to.

But the need should be high. If you dont really need than the effort
required for it isn't worth ;)

The double linked list with all it's bells and whistles was probably the
best thought thing I ever programmed.

It requires one to really thing everything through and doing it perfect
under all cases, miss one and it fails.

Anybody that has ever tried programming a linked or double linked list has
that experience

( me thinking of students gives me a big smell lol -> null pointer assigment
hehehe )

But really isn't a linked/double linked list a tool which any programmer
should have ;)

Ofcourse you can always use less powerfull lists of delphi (single linked
list, type specific, etc)

Or download it from the net.. but then running the risk of having bugs.

Anyway I like em a lot ;)

Only thing which is missing is multiple iterators... I only have one
iterator... which can ofcourse go out of scope

when a node is deleted and the iterator is on it.

I haven't yet found a nice solution for multiple iterators (can't really
remember) It probably requires a internal linked list of iterators for the
list itself
or maybe an array or maybe some other solution can be used.

I would be impressed by a double linked list which does have multiple
iterators and maybe even functions to indicate of the node which the
iterator is on is still valid. :D

Maybe one day I would code something like multiple iterators... I first
needed iterators for the merge sort walking from either end to the center,
solved that 'manually' ;) internal iterators ;) later came across some needs
for iterators... multiple nested for loops etc. I ended up using databases
and datasets instead ;)

Skybuck.
 
 
 

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by erewho » Sat, 21 Feb 2004 00:06:03

On Thu, 19 Feb 2004 15:43:42 +0100, "Skybuck Flying"


<snip>

Nuts !

Perhaps in a few years you will understand what cr*p you were talking
 
 
 

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by erewho » Sat, 21 Feb 2004 00:07:51

On Thu, 19 Feb 2004 15:33:55 +0100, "Skybuck Flying"


<snip>


Front it and grow them in lumps
 
 
 

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by Bruce Robe » Sat, 21 Feb 2004 00:38:22


delphi
finally

You are quite correct in believing that because you are bypassing the
standard functions provided by Delphi for dynamic management of records you
are also bypassing any management of dynamic types contained in those
records.

Why in the world are you using GetMem and FreeMem for records? Delphi has
New and Dispose for just this purpose and if you used them, as the help
tells you to, you won't have any worries. If you insist on keeping
Get/FreeMem, take a look at the help entry for the Finalize procedure.



Destructor Destroy; override;

destructor tForm1.Destroy;

begin
DestroyData;
inherited;
end;


DestroyData;
New (Data);


if Data <> nil
then begin
Dispose (Data);
Data := nil;
end;
 
 
 

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by Rob Kenned » Sat, 21 Feb 2004 01:07:54


Don't use GetMem. Use New. The memory that GetMem returns is not
initialized, so when you assign a value to the string field of the
record, Delphi will try to free the "string" that is already there. Any
non-nil value in that field will be interpretted as a valid string
reference, so the random value that GetMem leaves in that field, which
probably won't be nil, will try to be freed. The New procedure, on the
other hand, automatically initialized the record fields that require
initialization, such as strings. (An alternative is to call the
Initialize procedure -- see the help for details.)


Perhaps you could have guessed by now, but if you allocate memory with
New, you should release it with Dispose. Dispose will automatically
clean up any values that need cleaning up, such as strings (but *not*
objects). If you want to keep calling FreeMem, then use the Finalize
procedure beforehand.


Yes, that's exactly it.


All four hopes have been met.

--
Rob
 
 
 

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by Skybuck Fl » Sat, 21 Feb 2004 17:51:21

When it comes to dispose the delphi help is pretty unclear about what it
does.

Reading the post from somebody else I am guessing the dispose calls finalize
first
and then freemem or something :D

I'll check it all out :D
 
 
 

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by Skybuck Fl » Sat, 21 Feb 2004 18:00:26

This is a somewhat interesting program.

Which uses same dfm.

It will slowly create a string of 100 MB very funny to watch :D

Then when DestroyData is called ( dont call get data lol ) it is obvious
that the string
is not freeed... which is kinda weird.

Then when the program exits the string does get destroyed.

Now thx to you people suggestions I should now be able to create version
0.03

which will use dispose... and new ofcourse ojee have to remember that that
those go hand in hand !!!!

But but but but... how do I mention the size lol ?! oh yeah I have to use a
pointer type I guess... but what if I just want
to create some plain memory for a raw pointer ouch...

Oh well I'll see how it goes :D

Oh yes and thanks to finalize I should still be able to use getmem/freemem
which I do like a lot :D

So I will first try to finalize method in v3 :P :D =====D

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

{
version 0.02

creates a big 100 MB string takes long !

destroy data does not free the string but when program exits
it gets destroyed anyway
}


type
Pdata = ^Tdata;
Tdata = record
mAddress : string;
mMembership : boolean;
end;

type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Edit1: TEdit;
CheckBox1: TCheckBox;
Edit2: TEdit;
CheckBox2: TCheckBox;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
Data : Pdata;

procedure CreateData;
procedure DestroyData;
procedure SetData( const Address : string; const Membership : boolean );
procedure GetData( var Address : string; var Membership : boolean );
end;

var
Form1: TForm1;

implementation

{$R *.dfm}



procedure TForm1.CreateData;
begin
GetMem( Data, sizeof(Tdata) );
end;

procedure TForm1.DestroyData;
begin
FreeMem( Data, sizeof(Tdata) );
end;

procedure TForm1.SetData( const Address : string; const Membership :
boolean );
var
i : integer;
begin
// Data.mAddress := Address;

Data.mAddress := '';

// create a big string of exactly 100 MB
for i:=1 to 100*1024*1024 do
begin
Data.mAddress := Data.mAddress + 'A';
end;

Data.mMembership := Membership;
end;

procedure TForm1.GetData( var Address : string; var Membership : boolean );
begin
Address := Data.mAddress;
Membership := Data.mMembership;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
CreateData;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
SetData( Edit1.Text, CheckBox1.Checked );
end;

procedure TForm1.Button3Click(Sender: TObject);
var
s : string;
b : boolean;
begin
GetData( s, b );
Edit2.Text := s;
CheckBox2.Checked := b;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
DestroyData;
end;

end.
 
 
 

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by Skybuck Fl » Sat, 21 Feb 2004 18:40:26

"Rob Kennedy" < XXXX@XXXXX.COM > wrote in message
news:c12n0o$1dla0t$ XXXX@XXXXX.COM ...

Man that sounds painfull.... what would be the best way to prove this...

How can I make the app crash when it tries to do this...

What do you mean with free ? Dereferencing it ? Hmmm maybe if reference
count happened to be higher than zero.
yesyes, that's a good one.

So to make it crash I should probably try to mess around with the hidden
reference count before the string.

Hmmm also one other thing made me wonder in bed when I was thinking:

@S What does address of string return... ? the first character of the
string... or the location of the reference count or the length

Possibilities: 1. Location Reference Count
2. Location String Length
3. First string character

Let me check docs

While my system loads it let me tell you what I also thought about.... I
remember another situation where things got horrible for example:

var
p : pointer;

procedure IamCrazy;
var
s : string;
begin
s := 'when this procedure exists this string will no longer exist, which
will make the pointer point to thin air';

p := @s;

end;

procedure TestP;
begin

// p should blow up.... null pointer assignment or access violation or
something

ShowMessage( string( p^) ); // something like that untested code.


end;

Now I also though about a dirty solution...

The dirty solution would be to secretly increment the string's reference
count by 1... fooling delphi so it believes something
still references it... ofcourse this is dirty because no one has to manually
lower it... and pointer arithmetic trick is also a bit dirty
reference count location could probably change in other delphi versions or
so who knows...

But but but but but this raising a somewhat little interesting question if
you guys also new a 'legal' trick to keep the string alive but so that
delphi does destroy the string when the pointer goes dead or is reassigned
something else whatever.

If not then ofcourse the delphi developers could add extra run time
information to raw pointers, secretly keeping track of what the pointer
points to... if it points to something that is reference counted it will
behave like strings for example hehe.

I am not saying they should do this... because the pointer type is probably
used a lot where speed is needed... and adding this kind of type checking
/run time stuff would probably slow things down a bit... but if it is not
too much of a slow down then I would say go for it lol... that would be
super cool

Especially for dumbasses like me who shouldn't get addresses of strings and
assign it to pointerless types anyway... lol NOT !

I am just kiding ofcourse... assigning strings to pointers like that would
be super cool if it worked... because I could then hang strings in my double
linked list and I would not have to wrap them up in a fricking record or
class ===DDDD

Unless you guys know something which I dont know about pointers, strings and
reference counting.

Because lot's be honest for a sec :D Classes can be assigned to pointers !
by not strings ?! wtf ?! :D

It's a hole if you ask me :D it's a big damn ugly hole ! :D I can assign
anything to pointers without having to worry to much, but noooooo not
strings...
strings blow up in your face :D


Jipeeeeeeeee... I don't like new... it's so new...
 
 
 

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by Skybuck Fl » Sat, 21 Feb 2004 19:11:02

n short: Yes it's true... BIG ROB is the king ! :D

Yes it's true Delphi has a dirty little secret hehehe.

Or one could say: 'There is something about Delphi, lol'.

Does it have a tail ? noooo no tail... something much more horrible lol...

I don't care if you read this code + comments... but it's damn important
for those people that don't / or didn't know like me.

In short: If you don't know about this stuff... you could and statistics
theory dictates you WILL be fucked up your ass
hardddddddd by delphi ! It's nothing to be ashamed about lol... but it does
hurt a bit if it happens to you when it happens
and yes it will happen someday aaaannnddd then you ll be like... WTF?! why
does it suddenly crash... god damn it
wtf is wrong.. nononononononono, and then the system will go,
yesyesyesyesyesyou you have an exception ! you have an access violation
you suck, your applications sucks, yesyesyesyes, you suck, your application
sucks, you just spend developing 50 billion years for nothing... lol
all your debugging, all your testing, is totally for nothing because you
have an exception, you have an exception... and you know what I am going to
terminate you program, since I am the OS, since I am windows xppp.... I dont
like crashing... I am gonna crash you.. yesyesyesyesyes SO I LIVE ON
and YOU DIE... and all other NICE programs continue to live, and YOU my best
program will just FUCKING DIE ! yesyesyesyes
and I am gonna tell MICROSOFT about it yesyesyesyesyesyesyes SINCE YOU DID
NOT TURN OFF ERROR REPORTING YOU DUMB FUCK lol yesyesyesyesyesyesyes
(actually I turned off :P :D ) NONONONONONONONON you stupid that's what you
THINK ha-ha.. in secret
it's still on... wtf do you think all that network activatity on your cable
modem is all about... that's me sonny boy ! telling microsoft lol, about you
badbadbadbadbadbadbadbadb code/program... yesyesyesyesyesyes, do they care
no.... but it makes them allll laugh out you yesyesyesyesyesyes.

Yes, sooo now I am done with you, be GONE boom <- application removed from
memory/system.


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

{
version 0.02:

creates a big 100 MB string takes long !

destroy data does not free the string but when program exits
it gets destroyed anyway

version 0.03:

yep finalize with the ^ (dereference) operator does work and fucking
kills the string lol :D

version 0.04:

prove that delphi screws up when trying to assign a freshly new string
to the bullshit in the getmem structure/record.

YES YES YES YES it's true BIG ROB had BIG TRUTH on his lips ! euh
fingertips.

Yes BIG ROB I owe you one, but since I don't like owing people, you get
nothing
from me lol, except my congratulations with your excellent skills =D

}


type
Pdata = ^Tdata;
Tdata = record
mAddress : string;
mMembership : boolean;
end;

type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Edit1: TEdit;
CheckBox1: TCheckBox;
Edit2: TEdit;
CheckBox2: TCheckBox;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
Data : Pdata;

procedure CreateDat
 
 
 

Freeing a record which contains a string field leads to a memory/string leak ?!

Post by Skybuck Fl » Sat, 21 Feb 2004 19:15:51


upyours

lol dumb me... value should be re-fetched ofcourse:

ReferenceCount := integer( pointer( Cardinal(Data.mAddress) - ( 4 +
4 ) )^ );

tata.

detect