But isn't the same when you set OwnsObjects to False?
Main Topics
Browse All TopicsHi.
I want to put some objects in a TObjectList. I want to keep the objects in memory after this TObjectList is destroyed so I MUST keep the OwnsObjects= false.
I load the objects like this:
-------------------
GetCub is like this:
function TCubList.GetCub(Poz: Integer): TCubObj;
begin
Result:= TCubObj(self[Poz]);
end;
[Delphi 7]
This Question has been solved and asker verified All Experts Exchange premium technology solutions are available to subscription members.
Experts Exchange has been collecting answers to technology questions since 1996…3 million and counting! If you have a question, chances are we already have your answer.
If you can't find the exact answer you're looking for, ask our exclusive community of 50,000 experts. You’ll get a personalized answer from a trusted professional.
Thousands of free tech tips, tricks, how-to’s and tutorials are available in our peer reviewed articles section. See for yourself how smart our experts are, no login required.
Access the answers to your technology questions today.
30-day free trial. Register in 60 seconds.
Members of the expert community talk about why the experience at Experts Exchange is different than what you will find anywhere else.

Try it out and discover for yourself.
30-day free trial. Register in 60 seconds.
Join the community of experts here and help other tech pros by answering question in your area of expertise. You can earn FREE access to all Experts Exchange's premium features and resources.
I don't use FastMM so I cannot comment on that.
Your code does not show what Cub is. I aaume that it is a TCubObj Here are some things to try though to be honest I don't really think they should make a difference:
for x:= Lcub.Count-1 downto 0 DO
begin
Cub := TCubObj( LCub.GetCub(x)); // <--- Cast as TCubObj
if Cub.Vec.Count= 0 then then
begin
Cub.Free // <--- Try Free before Delete
LCub.Delete(x); // <--- Should do the same thing as remove it's just the way I would do it.
end;
Use Extract() instead Remove().
But remove should work... Are you sure that OwnsObjects is False?
Take a look in Notify() method of TObjectList. it free the object when Action:TListNotification is
lnDeleted but not when is lnExtracted.
If you look inside TList.Extract and remove you'll see that Extract send a lnExtracted Notifiy Action and Remove() sends a lnDeleted
Have you followed the usage instructions for FastMM4?
<quote>
Usage:
Delphi:
Place this unit as the very first unit under the "uses" section in your
project's .dpr file.
<end quote>
If you don't follow this, you may end up mixing the memory managers which could cause the issues you are seeing. Just a thought...
---
Russell
I use it as well, and have not had any issues with it. Most likely there is a pointer bug in your code. (stale pointer usage / double free / etc). I would suggest enabling full debug mode for FastMM. If you can narrow down the code (while still reproducing the error), then posting it here would also help.
Russell
This is caused because the memory manager has raised an EInvalidPointer exception.
>> from the help <<
EInvalidPointer is raised when an application attempts an invalid pointer operation. For example, it can occur if an application tries to dispose of the same pointer twice, or refers to a pointer which has already been disposed of.
<<
This type of error is ONLY thrown by the memory manager. As stated above, the most common occurrence is a double free on an object. It can also occur if a free'd object is later referenced, although it can be difficult to track. Take the following code (with intended pointer error) that executes without raising an exception.
var objList: TObjectList;
strmMem: TMemoryStream;
dwIndex: Integer;
begin
objList:=TObjectList.Creat
for dwIndex:=0 to 5 do
objList.Add(TMemoryStream.
for dwIndex:=Pred(objList.Coun
begin
strmMem:=TMemoryStream(obj
objList.Delete(dwIndex);
strmMem.Free;
strmMem.Size:=0; // <- This runs, but is a stale pointer BUG due to the object being freed
end;
objList.Free;
end;
There could be something wrong in the way I created those objects and insert them to the list? I REUSE the NewCub var to load more than one file from disk. Everything is fine. Right?
(full code into my initial post)
repeat
NewCub:= TCubObj.Create;
Error:= NewCub.ImportFile;
if Error<> '' then { if error loading this file then don't add it to the list }
begin
FreeAndNil(NewCub);
Continue;
end;
LCub.Add(NewCub);
until something;
I also tried to get rid of the NewCub var and work directly with the LCub list:
TCubObj(LCub[x]).ImportFil
but I get the same error.
--------------------------
I loaded two NewCubs into the LCub list and looked at their memory location using the @ operator.
Both have a unique address and also looking inside the object, all its data/fields are ok.
So, if the memory of those two objects is not overwritten, why I can't destroy them.
Note: If I remember correctly, in a test made few days ago, I can Free the first object (the one located at index 0 in LCub list) but not the last one.
PS: Points increased to 200.
" strmMem.Free;
strmMem.Size:=0; // <- This runs, but is a stale pointer BUG due to the object being freed"
rllibby
This is why I replaced one week ago all .Free operators with FreeAndNil, in the whole program (as you can also see in my initial piece of code).
From what I know, your code should immediately raise and error if the strmMem is free and NIL.
Anyway, the code where this problem appears is short and as you can see there is no place where a double call to FREE appears.
You have something going on... really hard to say without seeing full code.
Anyways, while my example SHOULD raise an exception, if you run it you will find that it DOES NOT. As already stated, pointer bugs can be insiduous and difficult to track. If you want to ensure that you don't have a double free going on behind the scenes, you could write a simple override class like below:
type
// Your existing class definition
TCubObj = class(TObject);
// Overriden example for testing
TCubObjEx = class(TCubObj)
private
// Private declarations
FFreeCount: Integer;
protected
// Protected declarations
public
// Public declarations
constructor Create;
destructor Destroy; override;
end;
constructor TCubObjEx.Create;
begin
// Perform inherited
inherited Create;
// Set free counter
FFreeCount:=0;
end;
destructor TCubObjEx.Destroy;
begin
// Resource protection
try
// Increment free counter
Inc(FFreeCount);
// Display message
MessageBox(0, PChar(Format('(%p) TCubObjEx.Destroy(%d)', [Addr(Self), FFreeCount])), nil, MB_OK);
finally
// Perform inherited
inherited Destroy;
end;
end;
then in places where you are using TCubObj.Create, you would use TCubObjEx.Create (or you could add the logic from above into your existing class, choice is yours). This will visually show you the number of times the destructor is getting called.
Russell
"Anyways, while my example SHOULD raise an exception, if you run it you will find that it DOES NOT" rllibby.
Did you actually tried to run that code? Because on my Delphi 7 it gave me this error (no FastMM):
--------------------------
Project1
--------------------------
Access violation at address 00000018. Read of address 00000018.
--------------------------
OK
--------------------------
If I activate FastMM, the AV is in "kernel32".
.
If I replace the FREE with FreeAndNil (as explained above) the error is:
--------------------------
Project1
--------------------------
Access violation at address 0041D6D1 in module 'Project1.exe'. Read of address 00000000.
--------------------------
OK
--------------------------
Sorry, I missed your post about checking for double free in the destructor.
Thanks for the idea. I will try it right away. If there is a double free attempt then your destructor should catch it.
One question: there is a special reason to override that class? Or I can insert the code directly into the TCubObj?
I'm running D5, and I never post code without first testing. If you had checked my background you wouldn't even ask that question. And you **MISSED** the whole point to begin with.
Because the heap is managed by Delphi (using default or fast MM), a free does nothing more than run the destructor and return the memory back to the managed heap. The memory is STILL allocated by the heap (unless / until the heap block is returned to the OS via a VirtualFree call), so in windows terms the memory is still valid. So while on your D7 system you get an exception, on a D5 system you don't. The **POINT** being that pointer bugs do not necessarily raise an exception on the line of buggy code. Did you even bother to check for double-free's using the code provided?
I would also suggest that you evaluate what you are asking for. You have pointer issues, and are expecting assistance without providing code to test/validate against.
"while my example SHOULD raise an exception, if you run it you will find that it DOES NOT"
Hey! My question was valid as the experiment shows something different that you were saying.
There is nothing wrong in questioning the information before entering it into the brain :)
If you say that you tested the code, I believe you. It just means that Delphi 5 and 7 behave in different way. There is no reason to get upset :)
---------
I will try the code you provided (the destructor) right away.
Is also my intention to fix this problem but I cannot upload the full program here. As I already explained it is really large.
I have placed your code into the Destructor and I have found that no objects are freed twice. So, this is a good.
However, I have found that all released objects have exactly the same address: 0012FC04.
Plus I have monitored the memory taken by the application. Every time I used that procedure that *may* have the bug, the memory raises from 6.22 to 20MB. But after the procedure exits, the memory returns to 6.22. I have run the procedure 20-30 times and the memory was always released. So I think there is no leak.
Show a little bit more code, something about the constructor of TCubList.
Can you make a little project which reproduces this behavior?
Also, try installing the trial of Eurekalog: www.eurekalog.com
It has a memory leak detection which works quite well.
Hi.
1) As I explained above, I tried to free the object directly, without calling Extract or Remove and is still not working.
It looks like there is a problem in freeing the TCubObj object and not in TCubList.
2) I didn't changed the constructor/destructor of TCubList. TCubList has just few extra properties that help me to access easily its items.
3) I think I stated this also above: only when I use FastMM4 (latest version) I have this "invalid pointer" error. If I remove FastMM, the program works perfectly (it worked for 3 years). No single other memory debugger can detect the leaks that FastMM detects (I have tried other 4, including Eureka).
4) As you have asked, the code will come soon.
MerijnB:
"what options do you have set for FastMM if you use it. Any more info in the exception (what addresses)?"
Is set on full debug - it needs the FastMM_FullDebugMode.dll.
"Invalid pointer op" appears as a dialog box. There is no address or other information. I have found its position in the source code by running the program step by step until that line where I try to free the 'Cub'.
This "invalid pointer" does not appear in FastMM's leak log.
the log may look like this, but is never the same:
--------------------------
This application has leaked memory. The small block leaks are (excluding expected leaks registered by pointer):
5 - 12 bytes: TIdThreadSafeInteger x 1
13 - 20 bytes: TList x 1
21 - 36 bytes: TIdCriticalSection x 2
37 - 52 bytes: TStringList x 2
85 - 100 bytes: AnsiString x 2
149 - 164 bytes: TCubObj x 2
165 - 180 bytes: Unknown x 1
437 - 484 bytes: AnsiString x 1
533 - 596 bytes: AnsiString x 2
821 - 916 bytes: AnsiString x 1
1013 - 1124 bytes: Unknown x 1
1669 - 1844 bytes: Unknown x 1
The sizes of leaked medium and large blocks are (excluding expected leaks registered by pointer): 162980, 109732, 4260
Note: Memory leak detail is logged to a text file in the same folder as this application. To disable this memory leak check, undefine "EnableMemoryLeakReporting
--------------------------
Again: Eureka sees no leaks, except the old one in a TDragAndDrop component - totally unrelated with this stuff (www.melander.dk).
something that strikes me as odd:
procedure TCubList.FreeObject(Poz: Integer);
var Cub: TCubObj;
begin
Cub:= GetCub(Poz);
FreeAndNil(Cub);
Delete(Poz);
end;
you write your own routine to free objects within TCubList when it's allready in the TObjectList ?
why ?
this is basically saying OwnsObjects := True ...
just set OwnsObjects = true and use the Delete(Index) or Delete(IndexOf(AObject));
something people invent something again and again and again and ...
when all they had to do was read the help to see if it existed allready or not
and when a program has a memory leak, and some other program catches it and points to something totally different, maybe the memory leak *is* in the something totally different ...
the second that i would say ...
at first glance you certainly have memory leaks
i tend to make sure i create an object and free it on the same level
i added levels to your code to show the level where you create and the level where your free
this is one of the ways to see visually where you have memory leaks ...
in 95% of the cases i just pinpoint the memory leak with keeping this in mind
because you deleted necessay parts of the code to understand the concept nobody can really help you
but some try ...
you are creating NewCub on Level1 and freeing it on Level2
lets translate it: you are allways creating it on Level1 and
(because of the if) sometimes deleting it on Level2
// level 1
{ IMPORT }
NewCub:= TCubObj.Create;
WITH FrmSettings DO
Msg:= NewCub.ImportSp(Name, spn1.Value, spn2.Value, spn3.Value);
{ REMOVE INVALID }
if Msg<> '' then
begin
// level 2
Out2Log('ERROR');
{ Move to 'INVALID' folder }
InvalidFolder:= ExtractFilePath(CntFullNam
if ForceDirectories(InvalidFo
begin
// level 3
if FrmSettings.radCopyFiles.C
FileCopyTo(NewCub.FullName
else
FileMoveTo(NewCub.FullName
end;
// level 2
InputList.Delete(InputSp);
FreeAndNil(NewCub);
CONTINUE; { ABORT THIS LOOP }
end;
// level 1
@Geert_Gruwez:
> you write your own routine to free objects within TCubList when it's allready in the TObjectList ? why ?
> this is basically saying OwnsObjects := True ...
see start post:
> I want to keep the objects in memory after this TObjectList is destroyed so I MUST keep the OwnsObjects= false.
There are of course other options for this, but this is fedra's motivation for doing this.
@fedra:
Geert_Gruwez is right with what he says above; it's a very good practice to keep creation and destruction of objects structured.
I noticed two problems in your code, if removed a lot of code below to make it clear where the problem might be.
1)
It's possible you add a nil pointer to your list here. If the last cub was removed because it was invalid (if Msg <> ''), you do FreeAndNil(NewCub).
But in the end, you always add the last NewCub to LCub.
2)
You (probably) let InputSp count from 0 until you break out of the loop (UNTIL InputSp>= InputList.Count). There is one problem though, you both delete from the list (InputList.Delete(InputSp)
This can cause a problem when you are at the last item. To avoid this, it's better to iterate through the list backwards (in other words, set InputSp to List.Count - 1 and keep going (deleting and/or increasing) until InputSp= 0).
> and you end up fixing code that actually needs deleting because a different approach must be used ...
This seems to be the case here. I think fedra just can't grasp it here, too many strange things in the setup which makes it hard to help.
On the other hand, this is the best way to learn things, just receiving a proper implementation isn't :)
> another thing i tend to avoid : CONTINUE and EXIT
> it breaks the natural flow !
Nice to notice the different opinions about this. I personally see this as very powerful flow control methods (if not abused of course). Just the same with goto, it's not evil by itself, but you can use it in an evil way.
i have drilled myself in finding the memory leaks on sight the way i explained above.
this resulted in not using exit, break, goto, continue, etc ...
i don't allways rewrite code, just when it gets very complex and when there are problems
i just reformat it, and usually after, it only takes a glance to see where the error is.
sometimes it does require some recoding, and then it's easy to go back to the original code
and pinpoint the error. try finally is often forgotten in this, and adding it usually pinpoints the error
like this
NewCub:= TCubObj.Create;
try
if x = y then
begin
finally
FreeAndNil(NewCub);
end;
end;
... compiler error !
maybe it would be better for fedra to state in a functional description what is desired of the code
What I want to do is to load and decode some files from disk, find the ones that are somehow similar and display them in a grid. In the piece of code (the top) that I displayed I filter invalid files (few of them may have been incorrectly formatted). I put remaining good files in the LCub. At the bottom of the code, I search for similar files and remove from LCub, those that are not similar. There are many filters like this. In the end only very few files are left in the LCub.
Then I transfer those remaining files from LCub into the grid, in order to display them. This is why LCub.OwnsObjects must be False.
Practically I use LCub as a temporary place to store my files (objects) until I find the files I am interested in.
so your LCub object does the parsing of the files
cleaner way to get to same level of create / delete
NewCub.ImportSp(FrmSetting
add a property/field to the TCub: IsValidFile: boolean
set it in the ImportSp
at the end of the Import:
delete the invalid ones
with functional description is easy to understand:
what about below approach for storing a list of *good* files ?
it's a bit of pseudo code (doesn't actually compile)
the goodfilelist is in the form and is available as long as the form exists
your proc for inserting into the grid would be very easy ...
in pseudo ...
procedure TForm1.PutinGrid;
var I: Integer;
begin
// Clean grid first off course
Grid.Recordcount := fGoodFilesList.Count;
for I := 0 to fGoodFilesList.Count-1 do
begin
recordIndex := AddRecord;
Values[recordIndex, columnName.Index] := TFileParser(fGoodFileList[
Values[recordIndex, columnFormatting.Index] := TFileParser(fGoodFileList[
// etc
end;
end;
no need to bother with freeing of objects any more as the TObjectList handles them
unless off course your ObjectList is not in the same form as your grid ?
you could still move it if it isn't ...
@Geert
at the end of the Import: delete the invalid ones
We are getting rally far away from the initial point of this thread. The problem is really complex; this is not a tiny 10000 SLOC program. I removed tones of code to make that snippet easy for you to understand it. Dont imagine the problem is as simple as listing the name of some files into a Grid. Very complex mathematical functions are applied to the content of the files. Even if you idea will be better, it will mean rewriting large portions of the program.
Anyway, among many other problems with your code, is that keeping the invalid files in memory until I finish the whole procedure will be a memory disaster. This is why I want to use FastMM.
In the snippet, I posted show only the parts relevant to LCub and NewCub. Lets stick to it.
Thanks anyway for trying. I am still interested to know if it is possible to free the object at the same level where you created. As you said, your code, dont demonstrate it.
Business Accounts
Answer for Membership
by: SteveBayPosted on 2008-12-11 at 10:21:41ID: 23150810
I think you would be better off using a TList rather than a TObjectList. TList holds pointer but does not own or manage the object it hold references to.