Fabulous Adventures In Coding

Eric Lippert's Blog

When Are You Required To Set Objects To Nothing?

NOTE: I have significantly revised this article.  For the original version, you can see the archived copy here: http://blogs.msdn.com/ericlippert/articles/136883.aspx

It's getting into sailing/kite flying season in Seattle -- that is, it's light out after work again -- and my home dev machine is presently busted (bad hard disk, grr), so I haven't had much time to work on SimpleScript.  I'll try to get the module system done over the weekend and start in on the name binders.  We'll see how it goes.

Until then, a quick follow up on my earlier entry on the semantics of Nothing in VBScript.  I'm amazed that I haven't blogged about this before; for some reason I just didn't get around to it until now.  I see code like this all the time:

Function FrobTheBlob()
  Dim Frobber, Blob 
  Set Frobber = CreateObject("BitBucket.Frobnicator")
  Set Blob = CreateObject("BitBucket.Blobnicator")
  FrobTheBlob = Frobber.Frob(Blob)
  Set Frobber = Nothing
 
 Set Blob = Nothing
End Function

What's the deal with those last two assignments?  Based on the number of times I've seen code that looks just like this, LOTS of people out there are labouring under the incorrect belief that you have to set objects to Nothing when you're done with them.

First off, let me be very clear: I'm going to criticize this programming practice, but that does NOT mean that you should change existing, working code that uses it!  If it ain't broke, don't fix it.

The script engine will automatically clear those variables when they go out of scope, so clearing them the statement before they go out of scope seems to be pointless.  It's not bad -- clearly the code works, which is the important thing -- but it needlessly clutters up the program with meaning-free statements.  As I've ranted before, in a good program every statement has a meaning. 

When I see code like this, the first thing I think is cargo cult programmer. Someone was told that the magic invocation that keeps the alligators away is to set objects to Nothing when you're done with them.  They do, and hey, it works!  No alligators!  (I'm sorry, Bert, I can't hear you; I've got a banana in my ear.)

Where the heck did this thing come from?  I mean, you don't see people running around setting strings to "" or integers back to zero.  You never see it in JScript.  You only ever see this pattern with objects in VB and VBScript. 

A few possible explanations immediately come to mind. 

Explanation #1: (Bogus) Perhaps some earlier version of VB required this.  People would get into the habit out of necessity, and when it became no longer necessary, it's hard to break the habit.  Many developers learn by reading old code, so those people would pick up on the old practice.

This explanation is bogus.  To my knowledge there has never been any version of VB that ever required the user to explicitly deallocate all objects right before they went out of scope.  I'm aware that there are plenty of people on the Internet who will tell you that the reason they set their objects to Nothing is because the VB garbage collector is broken.  I do not believe them.  If you've got a repro that shows that the GC is broken, I'd love to see it. 

Explanation #2: (Bogus) Circular references are not cleaned up by the VB garbage collector.  You've got to write code to clean them up, and typically that is done by setting properties to Nothing before the objects go out of scope.  Suppose you find yourself in this unfortunate situation:

Sub BlorbTheGlorb()
  Dim Blorb, Glorb
  Set Blorb = CreateObject("BitBucket.Blorb")
  Set Glorb = CreateObject("BitBucket.Glorb")
  Set Blorb.Glorber = Glorb
  Set Glorb.Blorber = Blorb
  '
  ' Do more stuff here
  '

and now when the procedure finishes up, those object references are going to leak because they are circular. 

But you can't break the ref by cleaning up the variables, you have to clean up the properties.  You have to say

  Set Blorb.Glorber = Nothing
  Set Glorb.Blorber = Nothing
End Sub

and not

  Set Blorb = Nothing
  Set Glorb = Nothing
End Sub

 Perhaps the myth started when someone misunderstood "you have to set the properties to Nothing" and took it to mean "variables" instead. Then, as in my first explanation, the misinformation spread through copying code without fully understanding it.

I have a hard time believing this explanation either.  Because they are error-prone, most people avoid circular references altogether.  Could it really be that enough people ran into circular ref problems AND solved the problem wrong to cause a critical mass?  As Tommy says on Car Talk: Booooooooooooogus!

Explanation #3:  It's a good idea to throw away expensive resources early. Perhaps people overgeneralized this rule? Consider this routine:

Sub FrobTheFile()
  Dim Frobber
  Set Frobber = CreateObject("BitBucket.Frobber")
  Frobber.File = "c:\blah.database"  ' locks the file for exclusive r/w access
  '
  ' Do stuff here
  '
  Set Frobber = Nothing ' final release on Frobber unlocks the file
  '
  ' Do more stuff here
  '
End Sub

Here we've got a lock on a resource that someone else might want to acquire, so it's polite to throw it away as soon as you're done with it.  In this case it makes sense to explicitly clear the variable in order to release the object early, as we're not going to get to the end of the scope for a while.  This is a particularly good idea when you're talking about global variables, which do not go out of scope until the program ends.

Another -- perhaps better -- design would be to also have a "close" method on the object that throws away resources if you need to do so explicitly.  This also has the nice result that the close method can take down circular references.

I can see how overapplication of this good design principle would lead to this programming practice.  It's easier to remember “always set every object to Nothing when you are done with it“ than “always set expensive objects to Nothing when you are done with them if you are done with them well before they go out of scope“.  The first is a hard-and-fast rule, the second has two judgment calls in it. 

I'm still not convinced that this is the whole story though.

Explanation #4: I originally thought when I started writing this entry that there was no difference between clearing variables yourself before they go out of scope, and letting the scope finalizer do it for you.  There is a difference though, that I hadn't considered.  Consider our example before:

Sub BlorbTheGlorb()
  Dim Blorb, Glorb
  Set Blorb = CreateObject("BitBucket.Blorb")
  Set Glorb = CreateObject("BitBucket.Glorb")

When the sub ends, are these the same?

  Set Blorb = Nothing
  Set Glorb = Nothing
End Sub

versus

  Set Glorb = Nothing
  Set Blorb = Nothing
End Sub

The garbage collector is going to pick one of them, and which one, we don't know.  If these two objects have some complex interaction, and furthermore, one of the objects has a bug whereby it must be shut down before the other, then the scope finalizer might pick the wrong one! 

(ASIDE: In C++, the order in which locals are destroyed is well defined, but it is still possible to make serious mistakes, particularly with the bane of my existence, smart pointers.  See Raymond's blog for an example.)

The only way to work around the bug is to explicitly clean up the objects in the right order before they go out of scope.  \

And indeed, there were widely-used ADO objects that had this kind of bug.   Mystery solved.

I'm pretty much convinced that this is the origin of this programming practice.  Between ADO objects holding onto expensive recordsets (and therefore encouraging early clears), plus shutdown sequence bugs, lots of ADO code with this pattern got written.  Once enough code with a particular pattern gets written, it passes into folklore that this is what you're always supposed to do, even in situations that have absolutely nothing to do with the original bug.

I see this all over the net.  Here's a random example that I got from Google: (lightly edited for clarity)

You can save an instance of a persistent object using its sys_Save method.
Dim status As String
patient.sys_Save
patient.sys_Close
Set patient = Nothing
Note that you must call sys_Close on an object when you are through using it. This closes the object on the server. In addition you should set patient to Nothing to close the object in Visual Basic.

Notice that calling the close method is a "must" but setting the variable to Nothing is a "should".  Set your objects to Nothing: it's a moral imperativeIf you don't, the terrorists have already won.  (One also wonders what the string declaration there is for.  It gets worse -- I've omitted the part of the documentation where they incorrectly state what the rules are for using parentheses.  This one page is a mass of mis-statements -- calling all of them out would take us very far off topic indeed.)

I would imagine that there are lots of these in the MSDN documentation as well.

What is truly strange to me though is how tenacious this coding practice is.  OK, so some objects are buggy, and sometimes you can work around a bug by writing some code which would otherwise be unnecessary.  Is the logical conclusion “always write the unnecessary code, just in case some bug happens in the future?”  Some people call this “defensive coding”.  I call it “massive overgeneralization“. 

True story: I found a performance bug in the Whidbey CLR jitter the other day.  There's a bizarre situation in which a particular mathematical calculation interacts with a bug in the jitter that causes the jitter to run really slowly on a particular method.  It's screwing up our performance numbers quite badly.  If I change one of the constants in the calculation to a variable, the problem goes away, because we no longer hit the buggy code path in the jitter. 

They'll fix the bug before we ship, but consider a hypothetical.  Suppose we hadn't found the bug until after we'd shipped Whidbey.  Suppose I needed to change my code so that in runs faster in the buggy Whidbey CLR.  What's the right thing to do?

Solution One:  Change the constant to a variable in the affected method.  Put a comment as long as your arm in the code explaining to future maintenance programmers what the bug is, what versions of the framework causes the problem, how the workaround works, who implemented the workaround, and how to do regression testing should the underlying bug be fixed in future versions of the framework.  Realize that there might be similar problems elsewhere, and be on the lookout for performance anomalies.

Solution Two: Change all constants to variables. And from now on, program defensively; never use constants again -- because there might someday be a future version of the framework that has a similar bug.  Certainly don't put any comments in the code.  Make sure that no maintenance programmers can possibly tell the necessary, by-design uses of variables from the unnecessary, pointless uses.  Don't look for more problems; assume that your heuristic solution of never using constants again is sufficient to prevent not only this bug, but future bugs that don't even exist yet.  Tell other people that “constants are slower than variables“, without any context.  And if anyone questions why that is, tell them that you've been programming longer than they have, so you know best.  Maybe throw in a little “Microsoft suxors, open source rulez!” rhetoric while you're at it -- that stuff never gets old.

Perhaps I digress.  I'd like to take this opportunity to recommend the first solution over the second.

This is analogous to what I was talking about the other day in my posting on Comment Rot.  If you hide the important comments amongst hundreds of trivial comments, the program gets harder to understand.  Same thing here -- sometimes, it is necessary to write bizarre, seemingly pointless code in order to work around a bug in another object.  That's a clear case of the PURPOSE of the code being impossible to deduce from the SYNTAX, so call it out!  Don't hide it amongst a thousand instances of identical really, truly pointless code.

Published Wednesday, April 28, 2004 11:37 AM by Eric Lippert
Filed under: ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Barry Dorrans said:

The good reason? ADO.

Lots of the ADO samples in MSDN contain the Set <object> = Nothing, and for good reason, you don't want to keep database connections open (connection pooling aside) or tables locked.

Of course there's always non-deterministic garbage control to through into the mix. Your object may fall out of scope, but who knows if it has been disposed yet. Not applicable to good old asp, but specifically calling dispose methods still applies in .net. Or does it? <g>
April 28, 2004 12:02 PM
 

Peter Ibbotson said:

Not as bad as the Dim x as new xyz problems you get in VB. The only circumstances I've ever set stuff to nothing rather than letting the runtime do it is when destroying large collections. If you do it yourself then you can put up a progress bar, otherwise it looks like the worlds stopped (and if it's started swapping to disk then life is really bad)
One possible reason is down to cut and pasting demo code which used to use globals perhaps?
April 28, 2004 12:05 PM
 

pds said:

In Access, it is 'convention' to do a .Close on all DAO.Recordset objects and DAO.Database objects, and set them = Nothing, like:

Dim rs as DAO.Recordset
Dim db as DAO.Database

Set db = CurrentDB()
Set rs = db.OpenRecordset("SELECT * FROM etc")
'etc
rs.Close
db.Close
Set rs = Nothing
Set db = Nothing


This is 'convention' because apparently there are/were bugs in earlier versions of Access (up to and including Access 97) where the Access window wouldn't close when you tried to close the application, no matter what you did. Ensuring your code always did the above fixed that problem.


Here's a Google thread where Michael Kaplan [MS] hints at the bug, if not explicitly so:

http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=active&selm=uLgaFdnX%23GA.226%40upnetnews05

Also, here's something written by Michael Kaplan, though it confirms the bug in a straightforward manner, it is debunked three lines later:

http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=active&selm=73i6a9%24158%40bgtnsc01.worldnet.att.net


So, in summary:
There are, or at least were, at one time, bugs that could be eliminated by ensuring we followed the 'convention'. Whether or not that is still the case, I don't know. Maybe get in touch with him; he's still around. Now I'm even more confused than when I began. But I'll still .Close/Set = Nothing everything until I hear different.
April 28, 2004 12:28 PM
 

Capt. Jean-Luc Pikachu said:

I don't think it's a myth because that's how we ended our query to a SQL 6.5 db from ASP 2.0. If we didn't, the db server would lock up, plain and simple.

These days? Probably unnecessary, but if the code works, who am I to complain?
April 28, 2004 12:56 PM
 

Kevin Westhead said:

Matt Curland has made several posts about this on various VB newsgroups, confirming that setting local variables to Nothing immediately before they go out of scope isn't required, although admittedly there may have been good reasons for this in older versions of VB. I think this was Eric's point, there are of course times when you want to explicitly set a local object to Nothing before it goes out of scope, but it's not necessary to do it just before a local object goes out of scope.

Some of Matt's arguments were:

1. Why not also call SysFreeString and SafeArrayDestroy on your string and array variables if you don't trust VB to clean up after you?
2. With statements generate hidden local variables, but you can't access them. If you use a With statement with an object, how are you going to set the hidden object to Nothing?
3. It's inefficient because VB runs the same code again as part of its normal teardown. In fact, explicitly setting objects to Nothing results in a vbaCastObj call whereas the normal teardown results in faster vbaFreeObj calls.

A collection of Matt's posts on the subject can be found here:

http://tinyurl.com/3ajhk
April 28, 2004 1:08 PM
 

RichB said:

I think the origin probably came about because most ASP pages were originally written without any functions/subs whatsoever. Therefore you would nearly always have statements following the last known use for an object ref.

Probably that and getting rid of expensive objects like DB connections, recordsets or COM+ objects early meant that the advice became prominent.

I've worked on sites where (despite my protestations) it was "best practice" to do this - and to write if statements like
if (foo) then
and not to have an Option Explicit statement at all. These sites weren't small sites - they were 100s of millions of hits per day sites that your granny uses.
April 28, 2004 1:15 PM
 

Bob Riemersma said:

See Coding Techniques and Programming Practices

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvsgen/html/cfr.asp

See 25+ ASP Tips to Improve Performance and Style

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnasp/html/asptips.asp

... et al. seemingly infinitum.

The point is that object references should generally be released as soon as they are no longer needed, and that by getting into this practice the programmer will be less likely to omit such an action where it really is needed. By doing this explicitly I suppose the theory was to make programmers be more aware of the process in general.

Trying to explain the semantics of object reference handling to people can be difficult because so many of them actually are cargo cult programmers. You don't want to see some of the scripts hacked together by box admins I've had to unravel, and Joe ASP can be just as scary a coder. Even funnier is trying to explain to a Javascript guy the difference between a Function and a Sub, and the syntax of calls in VB/VBScript. It doesn't help that the Windows Script CHM is so full of erroneous and/or misleading examples of VBScript calls.

The poor ASP coder is almost doomed from the start. It takes a whole lot of discipline to avoid some nasty spaghetti in ASP pages. I'm not sure they have a clue when an object reference variable goes out of scope. I'm not even sure anybody has defined "page scope" to these folks very clearly in the first place.

I suspect that in many cases people had trouble with data access objects because they would close a recordset but leave the underlying connection open.

Then there is the issue of connection pool management, but now I'm just being redundant. I think that was one of the core issues behind early release of object references in ASP pages and other pooled-connection scenarios.

Obviously the need to do explicit releases is situational. Out of scope is out of scope. I just think we're looking at a combination of a lack of understanding in some cases and an attempt to practice a "good habit" in others.

It's all a little reminiscent of the semicolon rules in C, C++, Java, and Javascript. The latter seems to be the most bizarre. Reading the ECMA spec it almost looks like the rule is "you need one when you need one except they're optional lots of places and times - and the script engine will take a stab when it feels like inserting one."

Oh, for the clarity of Algol 60 again!
April 28, 2004 1:38 PM
 

Bob Riemersma said:

Sorry to step on you RichB, my think time is nearly as long as my talk time I guess.

Why Option Explicit was not forced by default in ASP I'll never understand. You're right, I too know of ASP applications getting thousands of hits daily that fall over if you insert Option Explicit at the head of each page. Typically they're full of dead code too, not only server-side but in their client-side Javascript.

I just LOVE getting hauled in to unravel problems when they try to make small changes to these things and the house of cards comes down. Why companies don't hire outsiders to do code audits is beyond me, they'd save a bundle by stopping these things before their mission-critical application is down.

ASP.Net seems to have only made things worse, though I admit I'm dealing with all new screwball stuff there.
April 28, 2004 1:54 PM
 

Sjoerd Verweij said:

Easy: it's people not understanding scope.

When one does not understand scope, and sees/figures out that this is the right way to do things:

Private X As Object

Public Sub DoIt
Set X = New Foo
X.Bar
X.Close
Set X = Nothing
End Sub

This practice becomes "necessary" even in

Public Sub DoIt
Dim X As Object
Set X = New Foo
X.Bar
X.Close
Set X = Nothing
End Sub

Really. Try it. Next time you see this code, ask the author about scope.
April 28, 2004 1:55 PM
 

Eric Lippert said:

Re: Matt Curland posts -- thanks for the links to those. Matt is The Man. One of these days I'll tell you guys about a conversation Matt and I once had about the differences between VBScript and VB's default property resolution Intellisense logic -- it is ARCANE to say the least.

Re: ASP -- excellent point, and one that I had not considered. In ASP it is sometimes very difficult to know where you are and what scope you're in.

However, just to clarify, my beef is specifically with people who clear object variables IMMEDIATELY before they go out of a local scope. It is a really good idea to clear variables that hold expensive resources as soon as you're done with them if you're going to go do other stuff before they fall out of scope.

Re: Semicolon rules: I blogged about that already, see

http://weblogs.asp.net/ericlippert/archive/2004/02/02/66334.aspx

April 28, 2004 2:02 PM
 

Matthew said:

Eric,

I agree with you on this issue. Almost.

In some older versions of ADO (I don't know if the problem has since been fixed, or if the real root cause was Q255986), if you raised an error before explicitly cleaning up local ADO recordets (closing them and setting them to nothing) you got:

Method '~' of object '~' failed

This meant you needed to:

1. cache error details
2. clean up recordsets (close and set to nothing)
3. raise original error

Search Google Groups (Advanced Search) using Message ID uRi4ddceBHA.186@cppssbbsa01.microsoft.com (see particularly the 4th point).

Seeya
Matthew
April 28, 2004 8:59 PM
 

Enjoy Every Sandwich said:

Take Outs for 28 April 2004
April 29, 2004 12:28 AM
 

Jonathan said:

Setting objects to Nothing is a convention required by the company I work at, despite the fact that many advanced programmers here know it's not required. In fact, automatic cleanup is one of the better reasons to use VB at all, and having a convention to do it yourself makes the code a lot worse. I think our reasoning is that there are times where you have to set objects to nothing to deal with circular references, and that carried over into all uses of objects.

I almost started laughing when I saw that your "random example" was from Intersystems, as we use Intersystems Cache for our back end database. However, we don't use any of the VB-integration features that that web page refers to.
April 29, 2004 7:42 AM
 

Marcus Tucker said:

Just to chime in on the ASP side of things, back in the day when www.learnasp.com was the (virtually) definitive resource, the information distributed to the ASP coding masses was thus:
http://www.learnasp.com/learn/nothing.asp
(see also the links on that page)

I have personally always advocated explicitly closing (where applicable) and destroying objects because:

1) It was never made clear (in official Microsoft documentation) how effective/reliable the garbage collection was (anecdotal evidence at the time indicated that it wasn't too hot, but ADO Connection and Recordset objects probably muddied the waters)

2) It was never clear whether or not the garbage collection automatically calls the .Close method when deallocating an object that has such a method.

3) Explicitly closing & destroying objects *every* time (even when not strictly necessary, such as when it's about to go out of scope) makes for a more consistent coding style.

4) I thought that if there was a performance penalty to pay, it would be insignificant in the grand scheme of things, especially if it made apps more stable (i.e. a small price to pay)

5) I wrote myself a neatly encapsulated sub to do the work for me with minimum effort (see http://marcustucker.com/blogold/200403archive001.asp#1078920890001 )

6) It's hard work explaining how scopes work to newbies and so it's easier just to tell them to follow the rule (which I guess makes me guilty of encouraging cargo cult programming)


There are probably more reasons, but I can't think of them now! In any case, it's nice to have you (Eric and others) set the record straight for those that *really* want to know what's going on...
April 29, 2004 5:14 PM
 

Jon Fancey said:

hmmm. I think there is a bit of misunderstanding here about what setting something to nothing actually means whether you do it yourself or the runtime does it for you.

Eric you're right, you SHOULDN'T need to do this other than the valid reasons stated(and Matt C. is the definitive resource on this) - but this is only true if everyone sticks to the COM spec - basically they implement components with no bugs,

All you (or the runtime) is doing is asking the object allocated to release - IUnknown.Release. It doesn't have to. It is in control of its own lifetime - not you. Therefore any object is free to keep itself around as long as it likes due to the object controlling its own reference counting.

What VB(A) does is say, ok I KNOW I've got a valid object pointer here, and I know that cos the 'new' should have caused a ref counter to increment (either by doing a QI - set mything = new thing OR set mything = myotherthing) and I got a non-null pointer back (and a 0 HResult). What I can't guarantee is that when I call Release (= nothing) the object will go away.

I believe the reason that *many* people adopt this style in VB shops is because the behaviour can be different - ie you can force some components to free objects that otherwise wouldn't be released by the runtime and this is based on order.

COM is based on contractual adherence to the COM spec. However, there is no runtime enforcement of this spec (which is why .net is so cool). This opens up the possiblity that I can write a component that you call from VBA that will do something semantically different if you call set = nothing explicitly. Wow - you say, how is this? - we're both calling the same method (Release).

One reason is due to the clear-up order. You always free the top-level object (you don't even know about any others created by it). This should cascade down freeing everything else FIRST. i.e. the last object in the chain that is created is the first to disappear (you can free an object with extant refs as it's ref count is always > 0).

But what if you don't stick to this rule. Well, maybe calling release twice will make a difference if the component implementer screwed up their ref counting. Maybe set x = nothing yourself will affect the cleanup order and exhibit different semantics to the runtime. For example:

Dim obj1 as object
Dim obj2 as object

set obj1 = new class1
set obj2 = new class1

which one does the runtime release first? what about adding:

Dim obj3 as object
obj2.dostuff(obj3)

what's the order now. Because YOU can control the clearup order (to some extent) the result MAY be different to if the runtime does it.

I have seen the best practice of setting everything to nothing a hundred times. I think it's because there are a lot of component libraries out there with bugs in that don't implement the spec correctly. So, that fact that the runtime should release all the refs (and the spec effectively says the order in which this happens is irrelevant) - it might not be.

I remember data bound classes being an easy way to cause this kind of problem (and basically leak) if you didn't clear your own references explicitly.

I don't believe this is the only reason, anyone think of some more?
April 30, 2004 3:26 AM
 

Jon Fancey said:

Something else that I think muddied the waters a few years ago was when you stuck MTS or COM+ in front of the object you were creating. The client does a CreateObject, a wrapper in the interception runtime(MTS/COM+) is created and the 'real' object is owned by it not you. You get a ref to the wrapper back.

The complexity introduced makes non-deterministic cleanup (by the VBA runtime) really hard (like impossible) if there are bugs in anything created by it(either directly or indirectly). That's where a lot of the method ~ of object ~ errors (as mentioned above) used to come from because MTS had noticed that things had gone badly wrong - unexpected ref counts etc.
April 30, 2004 3:33 AM
 

Kim Gräsman said:

I quote:

"IServer::CreateObject keeps an internal list of objects it creates. When
the script execution ends, the Server object decrements the reference count
on the interfaces in this list. This doesn't mean that we shouldn't
explicitly garbage-collect when writing ASP, but if we forget, ASP will do
it for us."

This is from Jon Flanders' book 'ASP Internals' [1], which attempts to describe how ASP works and build an ASP engine for reference.

I sincerely hope this is wrong, or I haven't understood a thing about COM. I think Flanders has confused resource cleanup (Set obj = Nothing) with ASP's OnStartPage and OnEndPage callbacks.

FWIW, I believe I've heard the almighty Chris Sells comment on something similar as well, but I can't find the exact quote off-hand.

I'm glad to hear VBScript really is smarter than it gets credit for.

But I agree with previous posters; the main reason is probably ADO, and making sure resources are released asap. And then people just adopted it as a general rule.

[1] http://www.amazon.com/o/ASIN/0201616181
April 30, 2004 4:23 AM
 

Ben Lowery said:

In my former life, working for a semi-known eCommerce application company, we were deploying one of the first large applications built on top of ASP 3.0. It worked great in development, but we didn't stress test it before going live. Once live, the application would accept about 30 people per box and then flat hang. Turns out, connections were not getting dropped back into the pool, for whatever reason, and we were running out of available connections to the SQL Server.

The fix was to go in a add Set xxx = nothing for all of our ADO objects in all of our pages. After we did added all the Set xxx = nothing statements, the connection pool started acting as expected and we could put 200 ~ 300 people on a box at a time.

That was my first introduction to set xxx = nothing and I've been doing it ever since. :)
April 30, 2004 6:59 AM
 

Ron said:

Why? Because the VB runtimes would sometimes leave an object hanging out there. The primary example is with ADO components, but I've seen it with form and module level references as well. The rare instances objects weren't cleaned up are enough to make a developer second guess.

Thus, convention dictates we handle this "just in case". It's hardly ever needed at a procedure level. It's sometiems needed at a module level. It's almost always needed at global level.
April 30, 2004 7:37 AM
 

Eric Lippert said:

> IServer::CreateObject keeps an internal list of objects it creates

I have no idea whether that's true or not, and I haven't got the IIS sources handy anymore. I suppose that _could_ be accurate -- it could be that the server needs to hold onto the objects to implement some of the transactability semantics?

But I'm just guessing here. I would certainly have assumed that it did not hold onto any objects.

Next time I need to debug into IIS and pull the sources down I'll check it out.
April 30, 2004 1:06 PM
 

Kim Gräsman said:

> > IServer::CreateObject keeps an internal
> > list of objects it creates
>
> I have no idea whether that's true or not
> [...]

I've worked on an ASP emulator-ish product, and IServer::CreateObject needs to keep track of created objects in order to serve the OnStartPage/OnEndPage callbacks into objects it creates. It checks if these methods are available in the IDispatch layout, and if so, calls them.

This is for ASP components pre-COM+ with the IObjectContext integration, etc.

The idea that ASP needed to hold onto objects to garbage-collect them, seems insane, though. Reference counting is reference counting, and as far as I can see, there's nothing ASP can do to influence the script engine in that regard.
May 1, 2004 8:41 AM
 

Marcus Tucker said:

Another well-respected reference page which has some bearing on this subject is this:
http://www.15seconds.com/issue/010514.htm

As other posters have said, destroying objects ASAP probably became normal practice simply because of ADO. But there are plenty of badly-written COM objects around that might be suspect, and unless you've got the source who knows what might be left behind, so the accepted thinking is that it's preferable to take control and explicitly release resources as often as possible rather than leave it to what *should* be a foolproof garbage collection system in theory, but which is in reality likely to be less than perfect.
May 3, 2004 7:25 PM
 

Steve said:

I did some empirical testing using a C++ COM object that we created in-house. The object's FinalRelease gets called whether or not you set the object to nothing. FinalRelease gets called even when you call Reponse.End to end the script.
May 9, 2004 1:10 AM
 

Alex Angelopoulos said:

Interesting insights into the background of Nothingness...
For the present, in scripting circles, I would say it is now cargo cult stuff applied by imitation, like preceding each and every object name with an "obj". :)

May 11, 2004 10:12 AM
 

Bob Riemersma said:

Just to throw one last log onto this smouldering ash heap...

Take a gander at "BUG: Excel Does Not Shut Down After Calling the Quit Method When Automating from JScript"

http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/q266/0/88.asp&NoWebContent=1

It deals with the fallout from languages like JScript that don't provide a standard way to free object references.

The case here is a script that invokes Excel through Automation. In VBScript doing an XLApp.Quit doesn't result in Excel shutting down until you Set XLApp = Nothing or of course exit the scope of the variable containing the reference. In JScript you're just screwed unless you use the kluge CollectGarbage(), which may not be available depending on the JScript engine in use.

Again, this is the same sort of thing that can interfere with connection pooling or any of a number of other things.
May 13, 2004 7:58 PM
 

Fabulous Adventures In Coding said:

May 21, 2004 1:41 PM
 

Mike Gale said:

I guess when a programmer has spent hours figuring that Close then Set to Nothing is the answer to an otherwise incomprehensible bug that learning sticks forever. (I've also seen cases where the flakiness wastes days and I've even seen "new technologies" that consume three months of a programmers life before they're thrown out because of some gotcha.)

That's the danger of releasing flaky code. I'm with the defensive programmer on this. (Better programming that works than becoming an ex programmer in frustration!)
May 22, 2004 4:44 PM
 

RichB said:

Perhaps the answer to all this would have been for Microsoft to be open about the ADO bug and have fixed it immediately.

Therefore, to help prevent a similar practice in future, can we have a publically available .Net bug list? It should contain bugs that have not yet been fixed as well as bugs which have been fixed.
May 23, 2004 2:15 AM
 

Matthew said:

Bob Riemersma,

Your example re: Excel is a red herring and does not relate to the issue at hand.

As the MS support page clearly notes, the problem has nothing to do with the setting variables to Null / Nothing - it is to do with the fact that JScript uses garbage collection (while VB / VBScript uses reference counting).

I would expect similar behaviour from any garbage collecting environment (such as .NET).

You do state:

<quote>
In VBScript doing an XLApp.Quit doesn't result in Excel shutting down until you Set XLApp = Nothing or of course exit the scope of the variable containing the reference
</quote>

which is exactly Eric's point - setting XLApp = Nothing or letting it go out of scope are equivalent.

Seeya
May 23, 2004 7:12 PM
 

Brad Abrams said:

May 25, 2004 8:27 AM
 

spork said:

I would wager that this is due to one or both of the following: 1) an empirically derived rule, based on buggy automation implementations from "the good ol' days"; 2) well-meaning but wrong advice based on misunderstanding of scope, etc.

Someone previously commented that Excel sometimes would get stuck in memory when invoked from Jscript. It wasn't the only app. In the mid-to-late 1990s timeframe, lots of devs were learning to write automation objects, and making the requisite mistakes along the way. Perhaps stuck apps were due to actual bugs in their implementation. Or, perhaps the bugs were due to references getting parked in global variables or in some intermediate scope. Setting "o = Nothing" is black magic that works, so why not do it everywhere? Regarding my second point: I'm a C++ developer, so the idea of scope, ctors & dtors, etc makes sense. But, for folks who started out with QBasic or Pascal and moved to VB, perhaps the idea of destruction was new. Granted, these languages did have scope, but especially with Basic, the paradigm (when I learned it, at least) was "all globals, baby!"
May 25, 2004 1:41 PM
 

Michael D. Kersey said:

with apologies to Wm. Shakespeare...

If setting objects to Nothing not be mandatory, it nonetheless remains useful from a _documentation_ standpoint, since once an object is set to Nothing then the script should no longer make use of that object.

Those obsessed with the "cargo cult" metaphorical aspect of this practice may merely treat it as documentation, while those who continue to implement the practice for other reasons can do so freely.

And then we can all live happily ever after!8-))
May 28, 2004 1:36 PM
 

Eric Lippert said:

Indeed, self-documenting code is goodness!

But why stop at objects? Why not set strings and arrays and numbers back to their initial values to indicate that you're done using them?
May 28, 2004 1:40 PM
 

yor said:


I think that I located the source of this practice among VBScripters ... in the Holy Scripting Bible (otherwise known as SCRIPT56.CHM), there is a very oft used Method called "Run", and therein is an example at the end of said holy verse ...

=====

Example 2
The following VBScript code opens a command window, changes to the path to C:\ , and executes the DIR command.

Dim oShell
Set oShell = WScript.CreateObject ("WSCript.shell")
oShell.run "cmd /K CD C:\ & Dir"
Set oShell = Nothing

=====

Oops !!, it seems that this practice is sanctioned by the Scripting Bible itself !?!? It spread viral-like from the holy book itself ... ;o)

(i've often thought that if the SCRIPT56.CHM was updated - surely a *long* overdue task, the inconsistencies are confusing and **many** - then a lot of things could be cleared up just with a documentation revision !?) ;o)
June 3, 2004 4:45 AM
 

Phil Jollans said:

I know it is a bit late to comment on this blog, but I have just experienced one of those cases in VB6, where setting an object reference to Nothing made a difference.

In this case, I have a UserControl which implements an custom interface using the Implements keyword. A form using this control stores an interface pointer (of this custom interface type) to the control.

Initially, the VB6 application crashed on exiting. After setting the interface pointer to Nothing, the program no longer crashed on exiting.

In face, I have often seen this kind of problem, if you store an interface to a control on a VB6 form.

For example, if a form passes an interface to one of its controls to another form, then that other form had better free the interface. Otherwise the program will probably crash on exiting. (To be fair, this is a lousy practice anyway, but quite legal VB6).

I wish these cases did not exist, but it does seem naive to believe that they don't.

Phil
June 15, 2004 4:51 AM
 

Mark Hurd said:

Another use of this construct while debugging (similar to specifying the order of destruction as referred to above) is that it defines where the Terminate event occurs, allowing a more defined sequence of execution.

Of course, production destruction code shouldn't have these sort of dependencies...
July 11, 2004 12:52 AM
 

Mike S said:


Performance Question:

We make a large collection of many objects. It seems to take longer (several minutes) to clean up the objects than to create them (under a minute). How can we clean them up faster? We just want them to all go away.

We've tried similar tasks in C++ and Java and both are sub-second to create and delete millions of similar objects. VB takes about a minute to create and several minutes to delete.

We've tried letting VB clean up automatically, and we've also tried to explcitly set the objects and the collection to nothing. Nothing seems to get it going any faster.
July 15, 2004 9:06 AM
 

Eric Lippert said:

Clearly something weird is going on. But there's nowhere near enough information for me to diagnose the problem.

I would try running the slow code through a profiler and see what it comes up with. If that doesn't help, the best I can suggest is a support call -- make up a small, solid repro that demonstrates the problem, and call it in.
July 15, 2004 10:00 AM
 

Mike S said:

we've run side by side tests of simple samples. Creating 1 million instances of simple objects (3 members, 1 Str, 1 Double, 1 Int) and addint them to basic collections. We've then tried deleting the collection and deleting the objects individually.

We've done this in VB6, C++, and Java. Java and C++ complete the whole thing sub second. VB app takes almost a minute to create and several minutes to clean up. VB app freezes during the clean up but eventually completes.

The item above by Peter Ibbotson at 4/28/2004 12:05 PM sounds exactly like what we are experiencing. We just accepted that VB was this slow but are hoping that there is some way to delete these objects more efficiently.

July 15, 2004 2:58 PM
 

Eric Lippert said:

Same collection object in each case, or is it a different object in each?

My guess -- which, I will point out again, is a guess in total absence of actually seeing the scenario -- is that some collection objects are optimized for large sets and some are not, and perhaps that is the nature of the problem?

July 15, 2004 3:09 PM
 

Mike S said:

We used the class wizard to create Class1 with 3 properties: A string, a Double, and a Long.

Then used the class wizard to create a Collection of Class1 called Coll1.

Then used the code below to create instances of Class1 and add them to an instance of Coll1.

Up to 12,000 instances it is sub second and it is faster to delete than create (about .2 sec each).

Above 12,000 instances it is slower to delete than create.

At 100,000 instances it is 2 sec to create, 17 sec to delete.

At 160,000 instances it is 3 sec to create, 45 sec to delete.

Is there a better way?

Option Explicit

Private Sub cmdGo_Click()

Dim n As Long
Dim MyColl As Coll1

Dim tStart As Double
Dim tEnd As Double
Dim tElapsedCreate As Double
Dim tElapsedDelete As Double

Set MyColl = New Coll1

' Time Creation of the objects
tStart = Timer
For n = 1 To CLng(Me.txtN)
Call MyColl.Add(n, CStr(n), CDbl(n))
Next n
tEnd = Timer

tElapsedCreate = tEnd - tStart
'Me.txtCreateTime = CStr(tElapsedCreate)

' Time Deletion of the objects
tStart = Timer

Set MyColl = Nothing

tEnd = Timer

tElapsedDelete = tEnd - tStart
'Me.txtDeleteTime = CStr(tElapsedDelete)

MsgBox "Create: " & CStr(tElapsedCreate) & vbCrLf & "Delete: " & CStr(tElapsedDelete)

End Sub

July 15, 2004 4:25 PM
 

Eric Lippert said:

I believe you that its slow. I don't know whether the collection class was even designed to handle collections of that size.

My point though is that comparing this code against the "equivalent" code in C++ is not actually a valid comparison unless the C++ code uses exactly the same collection object. If the C++ code uses the same collection object AND it is still faster, then odds are very good that there's something wrong in the language engine. If it is just as slow, odds are very good that the problem is in the collection class.

What collection objects do you use in the C++ and Java benchmarks?
July 15, 2004 4:32 PM
 

Mike S said:

In C++ and Java the tests were done with regular lists, not the same Collection class used in VB.

But we've also tried a test in VB where we use a linked list rather than a collection. It is faster, but deleting the objects still takes much longer than it should.
July 18, 2004 8:08 PM
 

Eric Lippert said:

Then you're not comparing apples to apples, are you? If you want to find out what's slow, you don't change _everything_. You change _one thing at a time_.

Odds are very good that it's the collection object that's slow.

In any event, is this a meaningful benchmark? Are you actually writing a program that must handle millions of data which are allocated and deallocated frequently? In that case I would recommend that you either (a) use a language which allows you fine-grained control over memory lifetime -- like, say, C++, or (b) use a language which runs in an environment with a specially tuned garbage collector, like C# or VB.NET.
July 19, 2004 9:45 AM
 

Mike S said:

I wish we had done it in C++.

Our basic need is to create a list of structs to hold some data properties. We create them all at once, we use them, then we delete them all at once.

Creating and using them is pretty fast. It's just deleting them that takes long. Which makes me wonder if we are doing something wrong. But it sounds like we did things the accepted way and this is just the built-in penalty for using VB.

I was hoping there was some trick to speeding it up like there is for strings. We also need to concat large strings from many small parts. We started by using the STR1 = STR1 & STR2, which was slow. We replaced it with a string buffer and it sped up literally tens of thousands of times faster for very large strings.

But it sounds like no such workaround is available for objects.

July 19, 2004 6:42 PM
 

Pete said:

I know it's been a long time since this article was active but I had a 1/2c to throw in.  I think the underlying reason could be as simple as: lots of VB coders probably started life as C/C++ coders.  They knew they had to free their allocated objects before they went out of scope and can't get used to the idea that VB will do it for them.
February 14, 2006 11:17 AM
 

Xtreme said:

I am using field reference variables with the ADO recordset. I want to throw away the recordset early and do something more. So should i set the field reference variables to nothing before releasing the recordset or releasing the recordset will already release the fields correctly? I am a little bit confused that if this condition is similar to your Explanation #4.

Sub Something()
 Dim rs
 Set rs = con.Execute(..)

 Set fld1 = rs("field1")
 Set fld2 = rs("field2")

 '
 ' Do stuff
 '  

 ' Should i set fld1, fld2 to Nothing
 ' or the fields will be released when the recordset released?
 rs.Close
 Set rs = Nothing

 '
 ' Do more stuff
 '  
 
End Sub
June 2, 2006 6:20 AM
 

Ahmed said:

To Barry Dorrans : I disagree - if you have an open connection to a db you close it not set it to Nothing

connection.close which is different from what this article is talking about....

May 13, 2008 6:47 AM
 

object to nothing said:

June 25, 2008 7:42 PM
 

Connection Object selber schliessen oder nicht | hilpers said:

January 20, 2009 11:02 AM

Leave a Comment

(required) 
(optional)
(required) 
Submit

About Eric Lippert

Eric Lippert is a senior developer on the Microsoft C# compiler team. Before that he worked on the framework of Visual Studio Tools For Office. Before that, he worked on the compilers, runtimes and tools for VBScript, JScript, Windows Script Host and other Microsoft Scripting technologies. He lives in Seattle and spends his free time editing books about programming languages, playing the piano, and trying to keep his tiny sailboat upright in Puget Sound.

This Blog

Syndication


© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker