When reference counting goes wrong


Visual Basic always does reference counting behind the scenes whether you need it or not. For example, consider this code:

Dim toughluck As CHardway, toobad As CHardway
Set toughluck = New CHardway ‘ c = 1
Set toobad = toughluck ‘ c = 2
’ Use toobad...
Set toobad = Nothing ‘ c = 1
’ Use toughluck...

When toobad is set, the reference count is incremented. When toobad is destroyed, the reference count is decremented. But you can see that, in this case, the use of toobad is nested within the lifetime of toughluck. There’s really no need to mess with the reference count if you know the second reference is nested within the first. In C++ or B--, you can optimize this code by not doing the nested reference count. In Visual Basic, the reference count always happens behind the scenes whether you need it or not. This is no big deal because reference counting is cheap and situations in which you can determine at compile time that a reference is always nested are rare. Skipping this optimization in exchange for having the language take care of all the reference counting for you is a good trade-off.


In real Visual Basic code, references are rarely destroyed explicitly by setting an object variable to Nothing. Instead, they are created and destroyed automatically when you pass objects to procedures, return them from functions, set or get them with properties, or destroy other objects that hold references to them. Think of your program as a display panel in a science fiction movie where mysterious lights blink on and off in unintelligible patterns. You’re supposed to follow the plot of the movie, not try to figure out exactly what makes each light go on or off.


But there is one situation in which you have to understand and override the default reference counting behavior. That situation involves circular references. Let’s say you create object A. Object A creates object B. B and A have to communicate with each other, so A passes a reference of itself to B. B has a reference to A. A has a reference to B. Now you, the creator of A, have a reference to A, but you don’t have a direct reference to B.


Now suppose you’re done with A, so you attempt to terminate it, releasing your reference to A. You might wish that terminating A would cause A to release
its reference to B, but A has no way to do this. B still has a reference to A, so A’s reference count isn’t zero and it can’t terminate. B can’t terminate because A still has a reference to B. And now you, the creator of A, no longer have a reference to A. Both A and B live on as independent objects and you have no way to destroy either of them.


If you’re running in the Visual Basic environment, your program won’t terminate normally when you unload its forms. You have to hit the End button to kill it. The independent objects will continue to eat up resources even though you’re finished with them and don’t want them in memory. This can cause lots of problems—in extreme cases you might even have to use the Task Manager to terminate rogue EXE servers.