71

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
user692942
  • 14,779
  • 6
  • 66
  • 157
Ramon
  • 931
  • 1
  • 6
  • 10

7 Answers7

85

VB uses a so-called "reference counting" garbage collector.

Basically, the moment a variable goes out of scope, the reference counter on the referenced object is decremented. When you assign the object reference to another variable, the reference counter is incremented.

When the counter reaches zero, the object is ready for garbage collection. The object resources will be released as soon as this happens. A function local variable will most likely reference an object whose reference count never goes higher than 1, so object resources will be released when the function ends.

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

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

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.

Tomalak
  • 306,836
  • 62
  • 485
  • 598
  • 21
    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. – David-W-Fenton Feb 05 '09 at 21:32
  • 5
    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. – Tomalak Feb 06 '09 at 07:57
  • 1
    It's actually true, see http://support.microsoft.com/kb/164455 for instance. It's just good practice anyway. – Renaud Bompuis Feb 16 '09 at 10:35
  • 5
    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. – Tomalak Feb 16 '09 at 13:05
  • 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. – Jay Jun 16 '14 at 20:58
  • 4
    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 ... – Jay Jun 16 '14 at 21:01
  • 1
    ... 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. – Jay Jun 16 '14 at 21:05
  • @Jay 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. – Tomalak Jun 16 '14 at 23:11
  • 1
    @Tomalak 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. – Jay Jun 17 '14 at 04:46
  • 1
    @Jay 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. – Tomalak Jun 17 '14 at 08:05
  • 2
    @Tomalak I was replying to the comments, not to your answer. :-) – Jay Jun 17 '14 at 17:41
  • @Jay 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. – Tomalak Jun 17 '14 at 17:46
17

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.

BIBD
  • 14,299
  • 24
  • 78
  • 130
  • 4
    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. – Brandon Moore Mar 12 '12 at 19:18
  • 1
    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... – BIBD Mar 13 '12 at 12:32
  • 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. – BIBD Mar 13 '12 at 12:36
  • Thanks for the clarification. You are obviously correct, although one might argue that micro-optimization is not always worthwhile. – Brandon Moore Mar 14 '12 at 05:13
  • 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. – BIBD Mar 15 '12 at 14:08
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.

Shane Miskin
  • 1,873
  • 2
  • 22
  • 30
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.

david
  • 2,017
  • 17
  • 26
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."

John Mo
  • 1,276
  • 10
  • 13
1

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.

BobT
  • 11
  • 1
  • 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. – David-W-Fenton May 26 '10 at 22:48
  • 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. – BobT May 27 '10 at 08:32
  • 1
    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. – David-W-Fenton May 27 '10 at 17:44
-1

Try this

If Not IsEmpty(vMyVariant) Then
    Erase vMyVariant
    vMyVariant = Empty
End If