Is there a need to set Objects to Nothing [duplicate]
Asked Answered
H

8

80

I always read that it is recommended to set objects to nothing, once I am done with them. But I normally use them only in functions inside forms.

Isn't the reference lost and memory released when the function scope is left, regardless of setting objects to Nothing?

i.e. is it really necessary to do:

Set db = Nothing
Set record_set = Nothing
Hervey answered 5/2, 2009 at 17:47 Comment(2)
I realize that this question is four years older than the suggested duplicate. However, the accepted answer to this question contains an important flaw, which is that it mentions a "garbage collector" in VBA/VB6. Unlike in VB.NET, there isn't a garbage collector in these languages, the objects are destroyed in place as soon as they go out of scope, and this makes an observable difference (see the With block at the end). For this reason, I've closed this question as a dupe, and not the other way round.Breakaway
@Breakaway makes sense to me. But the dupe target is worded specifically for Excel... which is not really relevant but makes it appear to be less general. Can we edit that one to make it a more general canonical question?Mouldy
L
93

VB uses a mechanism called "reference counting" to determine if an object is ready for destruction.

When object reference is stored in a variable, e.g. when you use the Set keyword, the reference counter on that object is incremented. When the variable goes out of scope, the reference counter is decremented.

When the counter reaches zero, the object is ready for destruction. The object resources will be released as soon as this happens.

A function local variable will most likely reference an object whose reference counter never goes higher than 1, so object resources will be released when the function ends.

When you pass the object to other functions, or store it in longer-lived objects, the reference counter will go higher than 1.

Setting a variable to Nothing is the way to decrement the the reference counter explicitly.

For example, you read a file, and set the file object variable to Nothing right after the file.ReadAll() call. The file handle will be released immediately, you can take your time processing the content.

If you don't set to Nothing, the file handle might be open longer than absolutely necessary.

If you are not in a "must unblock valuable resource" kind of situation, simply letting the variables go out of scope is okay.

Libbey answered 5/2, 2009 at 18:22 Comment(15)
While everything you write is true (and well-said), the question is tagged MS Access, which means VBA. VBA in Access has historically had problems with not correctly updating the reference counts, so it is advisable as a matter of practice to explicitly clean up your object variables.Tamarra
I was not aware that there was any mentionable difference between VBA and VB 6.0 in this regard. I can't believe they wrote a new garbage collector and a new VB runtime just for MS Access.Libbey
It's actually true, see support.microsoft.com/kb/164455 for instance. It's just good practice anyway.Darreldarrell
The KB article does not indicate a different garbage collector is present in MS Access. It refers to a peculiarity in DAO, or in the tight connection Access and DAO have, that comes to light only if Access is used as an automation server.Libbey
Hmm, but there's a big difference between calling a close function on an object and setting a variable that references that object to nothing. If you have to explicitly call close, that means that when the object becomes eligible for garbage collection, but requires cleanup beyond simply freeing the memory, it does not do this clean-up automatically. If you have to set a variable to Nothing, that means that the system does not recognize that a variable has gone out of scope. Both are limitations in a gc system, but they are entirely different issues.Fonteyn
Is there anything authoritative on this? I can certainly believe that there could be bugs in a GC system so that it does not properly recognize when an object is eligible for GC. But the question is, is there actually a bug in VB such that a variable going out of scope does NOT properly decrement the use count, but setting the variable to nothing DOES properly decrement the use count. I am very skeptical of an approach to programming that says, "Well, it's possible that the compiler will generate incorrect code, so I'm going to write a bunch of extra code just in case." Where would this ...Fonteyn
... end? Like, should we put in two return statements for every function, just in case the compiler misses the first one? Should we write "y=x+x+x" instead of "y=x*3" just in case the multiply function doesn't work? Etc. Now if there is a known bug that VB does not properly handle reference counts when variables go out of scope and we need to code around that, okay, that's the sort of information I would very much like to hear. But I don't see anything that specific here.Fonteyn
@Fonteyn You are deliberately over-complicating things. y=x+x+x and y=x*3 are equivalent because those are primitive values, not objects. COM objects on the other hand do not allow explicit deconstruction anyway. Their allocation and de-allocation is outside of your control as a VBA programmer. A file object that goes out of scope (instead of being set to Nothing) will dispose itself properly upon garbage collection. If it didn't do that it would hardly be possible to call it COM-enabled. All I said is you can explicitly set things to Nothing. I did not say you must.Libbey
@Libbey I wasn't disagreeing with you, but with others on this thread. My point about y=x*3 had nothing to do with primitives versus objects, but simply with the idea some have expressed here that you should write additional code just in case the compiler and/or runtime environment will not work correctly. If you don't trust garbage collection, why do you trust arithmetic parsing? As I said, if someone can cite specific information that there is a bug in the compiler that we need to code around, ok. If not, writing code "just in case" the compiler doesn't work seems very unproductive to me.Fonteyn
@Fonteyn Then I'm not sure why you're ranting on my answer. :) I agree with you, writing any "just in case" voodoo is dumb and indicates that the respective author did not really understand what they were doing and that they were writing code with their fingers crossed instead.Libbey
@Libbey I was replying to the comments, not to your answer. :-)Fonteyn
@Fonteyn Since COM is VB's universal interface to virtually everything I'm prepared to accept that you can get the GC confused with badly implemented COM classes or circular references - much like IE6 leaked event handlers when they were created in a certain way.Libbey
@Libbey Yes, correct. The text is brackets is worded poorly compared to the text outside of the brackets. I think it would be better it I repost it as it cannot be edited.Breakaway
This answer makes it seem like there is a "garbage collector" module implemented in VBA/VB6 (there isn't), and that the events of the reference counter going to zero and the object being destroyed may be separated in time (the object is destroyed in place as soon as its reference count goes to zero). This difference is important, and without it e.g. this With block at the end would not work as expected.Breakaway
@Breakaway OK, makes sense. I've updated the wording of the answer to reflect that.Libbey
F
16

Garbage collection is rarely perfect. Even in .NET there are times where you are strongly encouraged to prompt the system to do garbage collection early.

For this reason, I explicitly both close and set to Nothing recordsets when I'm done with them.

Fen answered 5/2, 2009 at 18:16 Comment(6)
Do you mean .NET garbage collection isn't perfect in that its design is not always optimal or that there are bugs in the design? And do you have any references explaining circumstances under which you are advised to prompt early collection? Thanks.Boatload
One day a student came to Moon and said: “I understand how to make a better garbage collector. We must keep a reference count of the pointers to each cons.” Moon patiently told the student the following story: “One day a student came to Moon and said: ‘I understand how to make a better garbage collector...Fen
I'm not saying the garbage collector will never work without programmer intervention, just that it is imperfect and it can take a while for it to clean up. Explicitly getting rid of references when you no longer need them help the garbage collector.Fen
Thanks for the clarification. You are obviously correct, although one might argue that micro-optimization is not always worthwhile.Boatload
True... I'd never go back and audit/fix whole 100K link application. But if I'm coding something new, or there anyway fixing something else, I do it out of habit.Fen
Writing code that manually calls Close on everything and sets everything to Nothing correctly is extremely tiresome and error-prone. I would bet that the code you mention simply has these lines at the end of the method or the end of the loops, and that these lines are not in fact even called when an exception happens earlier in the method. That is often the case even with one object in the method; when there are 10 objects each of which should be manually destroyed at the right moment regardless of exceptions and execution flow, it becomes completely unmanageable.Breakaway
M
15

The very last line of the help topic for "Recordset.Close" in the Microsoft DAO help and the Access Developer Reference is this:

"An alternative to the Close method is to set the value of an object variable to Nothing (Set dbsTemp = Nothing)."

http://msdn.microsoft.com/en-us/library/bb243098.aspx

With that in mind, this article from the Microsoft Knowledge Base entitled "How to prevent database bloat after you use Data Access Objects (DAO)", tells you that you should explicitly close if you don't want your databases to bloat. You'll notice that the article is a little vague about the details; the "Cause" section is unclear, almost to the point of being gibberish.

http://support.microsoft.com/kb/289562

SYMPTOMS: A Microsoft Access database has begun to bloat (or grow rapidly in size) after you implement Data Access Objects (DAO) to open a recordset.

CAUSE: If you do not release a recordset's memory each time that you loop through the recordset code, DAO may recompile, using more memory and increasing the size of the database.

MORE INFORMATION: When you create a Recordset (or a QueryDef) object in code, explicitly close the object when you are finished. Microsoft Access automatically closes Recordset and QueryDef objects under most circumstances. However, if you explicitly close the object in your code, you can avoid occasional instances when the object remains open.

Finally, let me add that I have been working with Access databases for 15 years, and I almost always let my locally declared recordset variables go out of scope without explicitly using the Close method. I have not done any testing on it, but it does not seem to matter.

Marquesan answered 5/2, 2009 at 23:39 Comment(0)
V
4

When you are using ASP classic (server-side scripting), it is important to set all objects to nothing when you are through with them, because they do not go out of scope until the [virtual] server is shut down.

For this reason, all MS VB scripting examples always showed objects being closed and set to nothing. So that the script excerpts could be used in environments like ASP classic where the objects did not go out of scope.

There are, rarely, other situations where you wish to code long-running processes where the objects do not go out of scope, and you find yourself running out of physical memory if you do not explicitly release objects.

If you find yourself coding ASP classic, or running processes in global scope for some other reason, then yes, you should explicitly release objects.

Vouvray answered 9/8, 2016 at 12:36 Comment(0)
P
3

References are supposed to be cleaned up when the variable goes out of scope. Presumably this has improved with later versions of the software, but it was at one time not reliable. I believe that it remains a good practice to explicitly set variables to "Nothing."

Phineas answered 5/2, 2009 at 17:57 Comment(0)
C
2

I usually always put this at the end of my procedures, or call a "CloseRecordSet" sub with it in if I'm using module level ones:

Private Sub Rawr()
On Error GoTo ErrorHandler

    'Procedural Code Here.

    ExitPoint:
        'Closes and Destroys RecordSet Objects.
        If Not Recset Is Nothing Then
            If Recset.State = 1 Then
                Recset.Close
                Conn.Close
            End If
            Set Recset = Nothing
            Set Conn = Nothing
        End If
        Exit Sub

    ErrorHandler:
        'Error Handling / Reporting Here.
        Resume ExitPoint
End Sub

That way however the procedure ends, (be it normally or due to an error) the objects are cleaned up and resources are free.

Doing it that way is quite safe in that it you can just slap it in and it will only do what is necessary in regards to closing, or destroying the recordset / connection object, incase it has already been closed (due to a runtime error or just closing it early as ya should, this just makes sure).

Its really not much hassle and its always best to clean up your objects when you're finished with them to free up resources immediately regardless of what happens in the program.

Castro answered 26/5, 2010 at 15:15 Comment(3)
That's ADO code, no? ADO recordsets lack a State property, and you don't use connection objects. ADO doesn't have the reference counting problem that DAO does, so you don't need to clean up after. It's not like you should be using much ADO in an Access app, anyway -- outside an ADP, DAO is the preferred data access library except for the handful of things ADO does better.Tamarra
It is declared as an ADODB.Recordset, and does have a state property that defines whether it is currently open or not. Basically it checks whether it is already set to Nothing, and if not then checks if it is still open first (and closes it if not) using the state property, and then sets it to nothing after. This fully ensures it is closed fully and cleanly and can be used at any time within the procedure whether the Recordset is already open or not, nothing or not.Castro
My point is that ADO used from VBA does not have any of the reference problems that DAO does. You're cleaning up something that VBA will reliably clean up for you. That is, of course, assuming there's some justification for using ADO in the first place, which there very often is not.Tamarra
M
1

Eric Lippert (longtime Microsoft programming language designer) wrote about this topic in 2004. This article mentions VBScript, VBA, and VB6 mixed together, and his conclusions seem to pertain to all of them.

TLDR: He concludes:

  • No flaw in the language or compiler/interpreter. There is no need in the VB✶ languages to set objects to nothing explicitly, in general.
  • But there are specific cases due to complex program logic where this can be required. Programmers have overgeneralized this practice.

Here's a summary of the key points.

Firstly he cites some plausible-sounding but incorrect rationales:

Explanation #1: (Bogus) Perhaps some earlier version of VB required this.

...

Explanation #2: (Bogus) Circular references are not cleaned up by the VB6 garbage collector.

Neither is a correct assumption.

Next, some deductive reasoning for why some programmers may have adopted this practice too broadly:

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

...

I can see how overapplication of this good design principle would lead to this programming practice. ... I'm still not convinced that this is the whole story though.

IMO the most persuasive argument is the last which cites a known ADO issue that did in fact require explicit object cleanup:

Explanation #4: ... There is a difference [between clearing variables yourself before they go out of scope, and letting the scope finalizer do it for you]

...

[If] 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!

...

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.

He goes on to mention how even Microsoft documentation may have encouraged the Set = Nothing practice which then influenced numerous programmers who probably had no idea what the real original need for this was.

He's also rightfully critical that things got as far as they did:

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”.

Mouldy answered 15/4, 2023 at 14:50 Comment(0)
C
-2

Try this

If Not IsEmpty(vMyVariant) Then
    Erase vMyVariant
    vMyVariant = Empty
End If
Cussed answered 24/1, 2013 at 7:51 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.