I guess you might not know what the term “dangling pointer” means, but if you have ever done some more complex programming in Delphi (where you do not only put controls on a form and handle a few events – rather you create and use objects at run-time) you might have experienced weird Access Violations when you tried accessing properties of an object you though is “Assigned” (or not nil).
The term dangling pointer refers to a pointer that is not nil, but does not point to valid data (i.e. points to an invalid memory address).
Still confused? You’re not alone!
Take a look at the following piece of code:
TMyClass = class (TObject); … var mc_1 : TMyClass; begin mc_1 := TMyClass.Create; mc_1.Free; // is Assigned(mc_1) = TRUE? end;
You might expect that Assigned(mc_1) would return false, but this is not the case.
When you create an instance of a class, an object, the memory required is allocated on the heap. Calling mc_1.Free will free the memory occupied for the object referenced by mc_1. The variable mc_1 is not cleared out, it is not nil (i.e. is still Assigned) but the memory it points to is no longer valid.
The Assigned function tests if the pointer to the memory (the variable reference) is nil. In case of variable references, Assigned(mc_1) is equal to “mc_1<> nil”. mc_1 after the call to mc_1.Free is a dangling pointer.
Certainly, since the above is “your” code, you know you can no longer operate on mc_1 after you called Free on it. In case you want to be sure Assigned(mc_1) would return false, you would also need to do mc_1 := nil, or call FreeAndNil(mc_1). Case closed.
How about the next example:
var mc_1, mc_2 : TMyClass; …. begin mc_1 := TMyClass.Create; mc_2 := mc_1; mc_1.Free; mc_1 := nil; //mc_2 usable ? end;
Now, what is the state (i.e. can you use it) of the variable mc_2 after we have freed mc_1?
What if at some stage in the life of your application you need to do something with mc_2? Would you expect that Assigned(mc_2) would return false? Should mc_2 = nil be true?
The truth is that a call to “Assigned(mc_2)” as well as “mc_2 <> nil” will return true. 🙁
The problem here is that freeing mc_1 marks the memory block used by mc_1 as available – but all the mc_1 data is still there. Since mc_2 points to the same memory location where mc_1 used to be, the data is still there, Assigned(mc_2) returns true.
Therefore, mc_2 is a dangling pointer. But this one is a more complex one to handle! There is no way for you to check if mc_2 is usable!
The way how Delphi memory manager works, after freeing an object, is to mark the memory space occupied by the object as available – not clear it out in any way.
As you create new instances of your objects (various types), memory manager will reuse blocks of memory from the heap. Your dangling pointer points to the same address but this time, quite possibly, to a different object (different class instantiated at the same memory block).
What’s worst, if the new object is of the same type you could end up with this dangling pointer working perfectly! Consider where a newly instantiated mc_3 gets the memory space where mc_1 used to be. Using mc_2 you are not aware that mc_1 is dead, mc_2 works normally but operates on the mc_3 instance. Catastrophe.
Of course, it is more likely that some other object (of some other type) will reuse the memory space of mc_1 and you could start experiencing weird Access Violations when using mc_2. And still, Assigned(mc_2) returns true.
While there’s a lot discussion on this problem on the Internet, and while there are some posted solutions to check if a pointer points to a valid object instance, I am strong in my belief that there’s no way to check this with 100% certainty (am not considering third-party memory managers).
Is there a solution? No 🙁 Or, yes: be careful when you have such code constructs.
Rewrite the usage of mc_2 in a way that you somehow signal to mc_2 when mc_1 is freed (no longer really available).
In one of my programs I have the next setup (I guess common): an object has a property that is a list of objects of the same type. Something like:
//PSEUDO CODE: TMyClass = class public property Relatives : TObjectList<TMyClass>; end;
Now consider the following:
procedure TDanglingPointersForm.FormCreate(Sender: TObject); var mc : TMyClass; begin mc1 := TMyClass.Create; mc2 := TMyClass.Create; mc3 := TMyClass.Create; mc1.Relatives.Add(mc2); mc1.Relatives.Add(mc3); for mc in mc1.Relatives do begin //there are 2 that are ok (mc2 and mc3) end; FreeAndNil(mc2); for mc in mc1.Relatives do begin //mc that is mc2 will not be usable here !! //how to remove it from mc1.Relatives when mc2.Free ? end; end;
Of course, in a real world application all the code is not contained in one procedure!
My solution was to signal (using events) to the list when an object is freed – so that the list can remove it from itself.
Here’s the full source:
TMyClass = class; TMyClassList = class(TObjectList<TMyClass>) procedure Destroyed(sender : TObject); function Add(myObject : TMyClass): Integer; end; TMyClass = class private fRelatives : TMyClassList; fOnDestroyed: TNotifyEvent; function GetRelatives: TMyClassList; public destructor Destroy; override; property OnDestroyed : TNotifyEvent read fOnDestroyed write fOnDestroyed; property Relatives : TMyClassList read GetRelatives; end;
The TMyClass has a lazy-instantiated property “Relatives” which is actually a list of (other) TMyClass instances. Relatives does not own the instances added, they will be freed by other means.
Here’s the implementation part:
{ TMyClass } function TMyClass.GetRelatives: TMyClassList; begin if fRelatives = nil then fRelatives := TMyClassList.Create(false); result:= fRelatives; end; destructor TMyClass.Destroy; begin if Assigned(fOnDestroyed) then OnDestroyed(self); fRelatives.Free; inherited; end; { TMyClassList } function TMyClassList.Add(myObject: TMyClass): Integer; begin myObject.OnDestroyed := Destroyed; result := inherited Add(myObject); end; procedure TMyClassList.Destroyed(sender: TObject); begin if self.Contains(TMyClass(Sender)) then self.Remove(TMyClass(Sender)); end;
This time:
procedure TDanglingPointersForm.FormCreate(Sender: TObject); var mc : TMyClass; begin mc1 := TMyClass.Create; mc2 := TMyClass.Create; mc3 := TMyClass.Create; mc1.Relatives.Add(mc2); mc1.Relatives.Add(mc3); for mc in mc1.Relatives do begin //there are 2 that are ok (mc2 and mc3) end; FreeAndNil(mc2); for mc in mc1.Relatives do begin //only mc3 is here, as expected ! end; end;
Am eager to hear your ideas and solutions to this problem…
And that’s why smart programmers know that FreeAndNil is your friend, not your enemy.
FreeAndNil won’t help in the second example. Use FreeAndNil(mc_1) and it still leaves the dangling pointer mc_2. Misunderstandings like this inspire false confidence – in this case it’s your enemy.
(Bear in mind that the code in the second example has been simplified and that the creation, assignment and freeing of objects is usually spread around a single class).
I fail to see how FreeAndNil makes things worse here. I’ll wager that only a small proportion of code ever points two references at the same object.
I think a good solution (when usable) is to generate code by developing visually instead of hand coding. Hand coding introduces many problems and should be used sparingly. Of course you’ll need to hand code what you want to reuse visually. But, once you have it working correctly you shouldn’t need to hand code it again and it should work as expected from then on.
Hm …. am not sure I can follow you here. In most (complex) applications you will have more custom made classes than you would have objects you can visually generate (or whatever).
I’ve done whole applications without using any *new* classes. You can develop general classes that are usable across many application domains. Like DB objects that come with Delphi. They are general enough to be used for any database application because you can visually work with the fields of the dataset.
I love using objects (learned programming with OO) and use them a bunch everyday all day. But, you can generate classes and other object code visually. I know hand coding provides immense flexibility. But, visual development and reuse are the way to quality systems and reduced development time.
I get a lot of attacks for my view but I’m sticking with it. Someday you’ll all see the light. Also, if anyone is gonna’ throw tomatoes at me because I’m big on visual development please throw them slow enough for me to catch then salt and eat them.
One thing that would be nice in Delphi is the ability to generate all SQL code for add/edit/delete of “custom” objects. I just want to call TDatabase.Save(Object), TDatabase.Load(Object), etc.
Generate code visually may also be the highway to RAD hell… Awfully coupled UI and logic in your application. Unmaintainable.
I suspect you mean “visually”, i.e. use some tool to create classes, properties, links and sequences from some diagrams. I have never seen a project working when code was designed as such.
You can get rid of invalid pointers, by using some built-in Delphi features, like TComponent (and ownership), or reference-counted values (with Copy-On-Write).
Then properly handle instance lifetime, searching for any memory leak using e.g. FastMM4 leaking reporting feature.
When dealing with interfaces, since you do not have a garbage collector, you need to have at hand a “zeroing mechanism”, to ensure any circular reference won’t trigger such GPF.
And even with a Garbage collector, poorly written code may trigger some “null object reference” exceptions, even in C# or Java. Bad effect on customer side.
I don’t see how visually using UML to generate class code causes any problems.
I’m beginning to think I don’t view UI stuff like most people. It’s very easy to develop forms for user interaction that can be plugged in or pulled out for replacement with no issues.
I think that if I create reusable objects that have proper “leak” prevention in place. Then it will work all the time. I think that hand coding the same logic or data structures over and over again is what tends to introduce problems. How much can we expect people to act like robots? Robots are for use in doing repetitive tasks. People are for use in creating new things.
Nope, I am with ObjectMethodology.com in this case. I have done like this in practice in my job since 2005 and it works great of you have a framework that support it. We use Bold for Delphi and Model driven development in D2007 for a big transport logistic application. So we do changes in the UML model. That cause changes in the access layer to classes and database. In short it works great. The advantage is when working with big complex applications with many classes. We may have over 300 classes. But not all of them are in the database. For small applications the conventional development may be simpler,
Nice article… now if Delphi had Automatic Reference Counting, things will change considerably, but the change of some error will still hold. Just teasing…
-Marco
As far as I can tell, both ARC and weak references are planned for a future Delphi version.
You have traces in the XE3 RTL units that this move is being prepared: AUTOREFCOUNT define, [Weak] [UnSafe] [Ref] attributes, and so on…
See http://blog.synopse.info/post/2012/10/06/Delphi-XE3-is-preparing-reference-counting-for-class-instances
I have found and fixed some of these dangling and other invalid pointers by modifying the FastMM4.pas source: don’t really free (or mark as usable) the freed memory but only overwriting the allocated memory with $80 (default done by FastMM in debug mode). Of course, the program won’t run that long (memory usage is soaring 🙂 ) and becomes very slow (swapping by Windows) but sometimes it is the only way in a large program (with many different programmers working on it…)
Try SafeMM. It uses similar approach, but calls VirtualProtect with PAGE_NOACCESS flag instead of overwriting memory with $80.
In (very) early Objects in Turbo Pascal, you could create objects on the stack.
I don’t know how happened in background, but they were destroyed automatically when leaving the scope. Like how strings, for example.
Just use interface references instead of object references. Forget both Free and FreeAndNil, the references are always valid if a programmer did not nil then explicitly. An idea for a new flame “Use interface references instead of object references everywhere” 🙂
Interesting read and still a common problem regardless of FreeAnNil or not to FreeAnNil. Note that
TComponent has already a notification mechanism built in that is pretty similar to your example: see
When I have such errors, I usually add SafeMM to the project file. SafeMM is debugging memory manager, and it is quite good in situations you describe (of course not 100%). FastMM4 also has lots of debugging options.
Nice article Zarko! Personally, I have almost never had issues with dangling pointers because most of my pointer variables were local or a public pointer variable would usually point only to the object whose life time I would control and know when to use the pointer and when no to.
However, the points you have made are excellent. I know it seems counter intuitive to have a pointer not nil-ed once the object it points to is freed but it’s also logical that it’s not because freeing the object should take care of only the space it occupies. It’s like… when the house is demolished, the owner of the house can only take care of the land on which the house is built and not all the signs that point to the house, right?
If Assigned could be overloaded such that it…
1. Checks the validity of the address the pointer is pointing to
2. Checks the type of the address the pointer is pointing to
… then most likely such banal issues could be prevented.
Nirav
Zarko, your article explains well why I tend to do one of these two:
– Use [generic] object lists with OwnsObjects := True
OR
– Make sure I have my lists(or classes, etc) only in one place without pointer copies.
The only case where I do assignments like the ones you describe is when I have long
datamodule+dataset combinations(such as DMScheduled.CDSPatientMandateScreen for instance)
where you then have to do a FieldByName of a possibly long field name.
However, in those cases I know I don’t have dangling pointers(for long reasons to explain, but I am
always 100% sure); for these I use a “Dataset: TDataset” variable which cuts the typing short of half 🙂
It is indeed very dangerous to “play with pointers” if you’re not sure of what you’re doing.
Regards,
A
I always use nil initialization and then use FreeAndNil for any kind of pointers. this settles the mater. no more AV and no more dangling pointers
P1:=nil;
P2:=nil;
...
Pn:=nil;
try
P1:=TMyClass.Create;
Pi := Pj;
...
finally
FreeAndNil(P1);
FreeAndNil(P2);
... FreeAndNil(Pn);
end; //try..finally
I think this will not help in the case presented / explained.
I tried what you said and I got an invalid pointer operation error.
type
TStuff = class(TObject);
TThing = class(TObject)
Stuff: TStuff;
constructor Create;
destructor Destroy; override;
end;
constructor TThing.Create;
begin
inherited;
Stuff := TStuff.Create;
end;
destructor TThing.Destroy;
begin
FreeAndNil(Stuff);
inherited;
end;
var
A1, A2: TThing;
begin
A1 := TThing.Create;
A2 := A1;
FreeAndNil(A1);
FreeAndNil(A2);
end.
======
Error happens when trying to destroy A2. From what I understood A2 becomes a “Dangling pointer” once A1 is destroyed. Free+Nilling A1 doesn’t make A2 become nil so when you call FreeAndNil on A2 it thinks the memory A2 points to is still assigned – hence the error.
I guess the trick (according to Zarko’s idea?) was to hook up a notification to TThing that tells A2 to become nil once the memory it points to is freed.
Hi Žarko! It’s nice to see you are still there, sharing Delphi knowledge.
I’m sorry I didn’t read the whole article (usually I just read the header + code), but I have little time after work… If I correctly understood you want to explain how to resolve dangling pointers in Delphi way.
But this problem was years ago solved in VCL, why you didn’t mentioned that?
It was some time ago when I wrote my last Delphi code, but AFAIR:
1)
Basic version is implemented for TComponent(s):
mc1 := TMyClass.Create;
mc2 := TMyClass.Create;
mc1.MyRef := mc2;
mc2.FreeNotification(mc1);
FreeAndNil(mc2);
//…
FreeAndNil(mc1);
procedure TMyClass.Notification(AComponent: TComponent; Operation: TOperation); override;
begin
if (AComponent = FMyRef) and (Operation = opRemove) then
FMyRef := nil;
end;
2)
Better version: more universal but longer.
Uses bridge (kind of interface) classes for non-components (pseudo code)
AFAIR you can find this longer version somewhere in old VCL code (near D7) in TField related code.
Note that TMyClass & TOtherClass are indepentent in this case.
// MyClass.pas – interface to handler
TMyNotifyHandler = class
procedure Destroying(Sender: TMyClass); virtual; abstract;
end;
TMyClass = class
public
procedure RegisterRelative(A: TMyNotifyHandler);
procedure UnregisterRelative(A: TMyNotifyHandler);
destructor Destroy; override;
private
procedure NotifyDestroying;
private
FMyRelatives: TList (of some form);
end;
// OtherClass.pas – uses MyClass.pas
TMyNotifyHandler4OtherClass = class(TMyNotifyHandler)
public
constructor Create(Owner: TOtherClass);
procedure Destroying(Sender: TMyClass); override;
destructor Destroy; override;
procedure SetObserved(A: TMyClass);
private
FOwner: TOtherClass;
FObserved: TMyClass;
end;
TOtherClass = class
public
constructor Create;
destructor Destroy; override;
procedure HandleMyClassDestroy(Sender: TMyClass);
procedure ConnectMyClass(A: TMyClass);
procedure DisconnectMyClass;
private
FNotifier1: TMyNotifyHandler4OtherClass;
end;
//———————————————————————-
procedure TMyClass.RegisterRelative(A: TMyNotifyHandler);
begin
FMyRelatives.Add(A);
end;
procedure TMyClass.UnregisterRelative(A: TMyNotifyHandler);
begin
FMyRelatives.Remove(A);
end;
destructor TMyClass.Destroy;
begin
NotifyDestroying;
FMyRelatives.Free;
inherited;
end;
procedure TMyClass.NotifyDestroying;
begin
for each (FMyRelatives)
FMyRelatives[i].Destroying(self);
end;
//———————————————————————-
constructor TMyNotifyHandler4OtherClass.Create(Owner: TOtherClass);
begin
inherited Create;
FOwner := Owner;
end;
procedure TMyNotifyHandler4OtherClass.Destroying(Sender: TMyClass);
begin
if Sender = FObserved then
begin
FOwner.HandleMyClassDestroy(Sender);
FObserved := nil;
end;
end;
procedure TMyNotifyHandler4OtherClass.Destroy;
begin
if assigned(FObserved)
FObserved.UnregisterRelative(self);
end;
procedure TMyNotifyHandler4OtherClass.SetObserved(A: TMyClass);
begin
if Assigned(FObserved)
FObserved.UnregisterRelative(Self);
FObserved := A;
if Assigned(FObserved)
FObserved.RegisterRelative(Self);
end;
//———————————————————————-
constructor TOtherClass.Create;
begin
FNotifier1 := TMyNotifyHandler4OtherClass.Create(self);
end;
destructor TOtherClass.Destroy;
begin
FNotifier1.Free;
end;
procedure TOtherClass.HandleMyClassDestroy(Sender: TMyClass);
begin // can be handled optionally
end;
procedure TOtherClass.ConnectMyClass(A: TMyClass);
begin
FNotifier1.SetObserved(A);
end;
procedure TOtherClass.DisconnectMyClass;
begin
FNotifier1.SetObserved(nil);
end;
//———————————————————————-
// usage
//———————————————————————-
mc1 := TOtherClass.Create;
mc2 := TMyClass.Create;
mc1.ConnectMyClass(mc2);
FreeAndNil(mc2);
//… mc1 doesn’t have reference right now to mc2
FreeAndNil(mc1);
Pingback: Cocosistemas.com. Diego Muñoz. Informático Freelance. » Blog Archive » Navegaciones semana 06/01/2014-12/01/2014
The real question is why on earth Embarcadero does not provide a normal function instead of the phony Assigned()??????