views:

669

answers:

14

Working on a number of legacy systems written in various versions of .NET, across many different companies, I keep finding examples of the following pattern:

public void FooBar()
{
    object foo = null;
    object bar = null;

    try
    {
       foo = new object();
       bar = new object();

       // Code which throws exception.
    }
    finally
    {
       // Destroying objects
       foo = null;
       bar = null;
    }

}

To anybody that knows how memory management works in .NET, this kind of code is painfully unnecessary; the garbage collector does not need you to manually assign null to tell that the old object can be collected, nor does assigning null instructs the GC to immediately collected the object.

This pattern is just noise, making it harder to understand what the code is trying to achieve.

Why, then, do I keep finding this pattern? Is there a school that teaches this practice? Is there a language in which assigning null values to locally scoped variables is required to correctly manage memory? Is there some additional value in explicitly assigning null that I haven't percieved?

+2  A: 

I've seen this a lot in VBScript code (classic ASP) and I think it comes from there.

Tim Coker
Does it have value in VBScript?
Programming Hero
Its basically null. I've Googled it since posting, and that's where the practice originated. In VBScript, setting the value to nothing released the reference to the object allowing the resources to be reclaimed (basically forcing the object to not have any references). I'm not 100% sure if it was necessary in ASP, but every page I've seen claims its a good practice.
Tim Coker
VBScript probably (like VB6) has deterministic memory management with reference counting, and setting something to null immediately decrements the reference counter. However, I wonder where this could make a difference in those simple, short-running scripts that VBScript is intended for.
Philipp
I'm somewhat saddened to know that this, indeed, is the correct answer.
Noon Silk
Its use in the VB variants was quite limited. In most cases setting a reference to `Nothing` was useless because that happens automatically when a reference goes out of scope, but there were a few special cases where doing it greedily was useful. In my VB6 days I noticed that developers were doing it unnecessarily quite often. The more things change the more they stay the same :(
Brian Gideon
Its "use" as in "utility" (ie, did it do anything useful) was likely limited, but the practice of obj = Nothing was very, very widespread... All comes back to FUD and CYA in the end, though. "Better to have it and not need it than need it and not have it."
Tim Coker
+2  A: 

I think it used to be a common misunderstanding among former C/C++ developers. They knew that the GC will free their memory, but they didn't really understand when and how. Just clean it and carry on :)

Grzenio
+3  A: 

It comes from C/C++ where explicitly made setting your pointers to null was the norm (to eliminate dangling pointers)

After calling free():

#include <stdlib.h>
{
    char *dp = malloc ( A_CONST );

    // Now that we're freeing dp, it is a dangling pointer because it's pointing
    // to freed memory
    free ( dp );

    // Set dp to NULL so it is no longer dangling
    dp = NULL;
}

Classic VB developers also did the same thing when writing their COM components to prevent memory leaks.

Justin Niessner
To be fair, setting local variables to `NULL` at the end of scope in C++ would be just as unnecessary as it is in C#.
Programming Hero
+1  A: 

It comes from C/C++ where doing a free()/delete on an already released pointer could result in a crash while releasing a NULL-pointer simply did nothing.

This means that this construct (C++) will cause problems

void foo()
{
  myclass *mc = new myclass(); // lets assume you really need new here
  if (foo == bar)
  {
    delete mc;
  }
  delete mc;
}

while this will work

void foo()
{
  myclass *mc = new myclass(); // lets assume you really need new here
  if (foo == bar)
  {
    delete mc;
    mc = NULL;
  }
  delete mc;
}

Conclusion: IT's totally unneccessary in C#, Java and just about any other garbage-collecting language.

dbemerlin
Assigning a pointer to null didn't free the reasources, it just destroyed your pointer to it, resulting in memory leaks. In C#, I would still recommend using destroy or close methods.
Vladimir Kocjancic
@Vladimir: I think thats what i implied but ok, the update should clarify it.
dbemerlin
That would be more along the lines of `foo.Dispose(); foo = null;` in C#, not what we're talking about here.
Ben Voigt
But that still might be where it came from.
Matt Ellen
@Ben: we are talking about where setting objects to null before leaving a method comes from, not what it means or would be in C#
dbemerlin
@dbemerlin: That's my point -- the example doesn't show setting object references to null before leaving a method, it shows setting pointers to NULL after *deleting* them.
Ben Voigt
A: 

VB developers had to dispose all of their objects, to try and mitigate the chance of a Memory leak. I can imagine this is where it has come from as VB devs migrated over to .NEt / c#

Chris
The old VB had reference counting as a deterministic memory management method. So it was never necessary to manually nullify something, it would have been done automatically whenever the variable went out of scope. But sometimes one did manually set variables to null to decrease the reference count (and possibly destroy them) prematurely.
Philipp
+3  A: 

It is more common in languages with deterministic garbage collection and without RAII, such as the old Visual Basic, but even there it's unnecessary and there it was often necessary to break cyclic references. So possibly it really stems from bad C++ programmers who use dumb pointers all over the place. In C++, it makes sense to set dumb pointers to 0 after deleting them to prevent double deletion.

Philipp
This happened frequently in pre .Net VB. I think it was generally (when I encountered it, anyway) a fear reaction to having been burnt by circular references resulting in memory leaks in the past.
Greg D
Thanks, I completely forgot these circular references.
Philipp
Only problem: you can't form a referential cycle with local variables. Make sense to reset references held in member variables, in Java and C# as well as VB6.
Ben Voigt
Oh yes, you'll notice that I haven't worked with reference-counting systems for a long time ;-)Maybe the requirement to unset member variables just spilled over to all kinds of variables?
Philipp
Speaking as someone who has spent entirely too much time reading old VB code, this was a ridiculously common idiom within that language, especially when dealing with COM (er, ActiveX... er, OCX...) references.
Shog9
@Shog9: do you know where this idiom came from? Simply incompetence or something else?
Philipp
@Philipp: I never got a satisfactory explanation for it... At the time, I put it down to the sort of cargo-cult mentality that was pervasive within VB culture, but in hind-sight it may simply be that VB didn't offer terribly good tools for tracking down reference leaks, and when you're working with a legacy-code monstrosity full of implicit variable declarations and re-used globals, it was the only way to be *sure* you weren't hanging on to references long past the point where they should have been released (important when those references are attached to database connections and the like).
Shog9
+1  A: 

May be the convention of assigning null originated from the fact that had foo been an instance variable instead of a local variable, you should remove the reference before GC can collect it. Someone slept during the first sentence and started nullifying all their variables; the crowd followed.

Amarghosh
A: 

I can see it coming from either a misunderstanding of how the garbage collection works, or an effort to force the GC to kick in immediately - perhaps because the objects foo and bar are quite large.

ChrisF
+19  A: 

It's FUDcargo cult programming (thanks to Daniel Earwicker) by developers who are used to "free" resources, bad GC implementations and bad API.

Some GCs didn't cope well with circular references. To get rid of them, you had to break the cycle "somewhere". Where? Well, if in doubt, then everywhere. Do that for a year and it's moved into your fingertips.

Also setting the field to null gives you the idea of "doing something" because as developers, we always fear "to forget something".

Lastly, we have APIs which must be closed explicitly because there is no real language support to say "close this when I'm done with it" and let the computer figure it out just like with GC. So you have an API where you have to call cleanup code and API where you don't. This sucks and encourages patterns like the above.

Aaron Digulla
Thanks for the hint to cyclic references, this is in fact the most probable explanation for manually nullifying variables in the old VB and similar reference-counting systems.
Philipp
The same happened in early versions of Python as well. You see the same pattern in Java by all those C/C++ programmers who long for `free()`, or the C++ guys who try to avoid reuse of destroyed resources. I often use it for the same purpose: `resultSet = DBUtil.close(resultSet)`
Aaron Digulla
I'll accept this. The need to assign `null` to escape circular references in older GCs sounds like it was a useful practice.
Programming Hero
This business of having objects you have to explicitly clean up reminds me of `IDisposable`. Though I guess with an object implementing `IDisposable`, you'd better damned well call its `Dispose()` method.
Tim Coker
@Tim Coker: To be more precise, you should should call Dispose using `using`. Explicitely calling `Dispose()` is ugly, since you need it to be in a `finally` block and it adds a lot of excess code.
Brian
@Brian - Calling dispose in a finally block is sometimes necessary, but, yes, it is not preferred.@Tim - I had a support call with MS just a few days ago and the guy tried to tell me that calling dispose was largely optional. I could not believe it, and when pressed, he recanted.
StingyJack
Calling `Dispose` is certainly optional. If you don't do it, the GC will effectively do it for you, just not when you want it to. And `using` is only the right thing to do when your disposable object doesn't leave your scope. See Rx's `IObservable.Subscribe` for a counterexample.
Gabe
@Gabe: Maybe it's optional in .NET but in Java, you have to call close/destroy yourself in the right place.
Aaron Digulla
Aaron: In Java you can leave files open as long as you want. If you never close them, they are guaranteed to get closed at some point after they are no longer referenced.
Gabe
To be picky about the terminology, FUD refers to marketing practises by software companies, see http://en.wikipedia.org/wiki/Fear,_uncertainty_and_doubt - what you're talking about here is "cargo cult programming", see http://en.wikipedia.org/wiki/Cargo_cult_programming which actually mentions "adding deletion code for objects that garbage collection would have collected automatically with no problem" as an example.
Daniel Earwicker
A: 

It comes from C++ code especially smart pointers. In that case it's rougly equivalent to a .Dispose() in C#.

It's not a good practice, at most a developer's instinct. There is no real value by assigning null in C#, except may be helping the GC to break a circular reference.

Wernight
What should this have to do with smart pointers? Setting pointers to null seems like the opposite of RAII to me. And it's not at all equivalent to `Dispose`. Smart pointers like `unique_ptr` or `shared_ptr` implement a deterministic memory management model where resource disposal is handled by the RAII pattern. Since RAII is impossible in nondeterministic memory management models, some kind of `Disposable` pattern is required there.
Philipp
This is just plainly wrong.
Dykam
As part of the question is: "Is there a language in which assigning null values to locally scoped variables is required to correctly manage memory?", yes, C++ smart pointers do. It's a kind of memory management (without garbage collector). Nothing in the post seem to indicate that those `= null` didn't come from a modified copy/paste (after call MSVC even supports a `__finally`).
Wernight
@Dykam: Would you mind explaining a bit more?
Wernight
@Wernight: The answer is "No, absolutely not." Setting smart pointers to NULL in C++ is definitely not necessary, when the smart pointer destructor is called as it goes out of scope (and destructors are called during stack unwind of exceptions, they get "finally" treatment automatically) it will free the target.
Ben Voigt
@Ben: I didn't say it was necessary. Neither is calling "Dispose()" *necessary*. In your example since their scope is outside of the `try{}finally{}`, it's not automatically called. Now what's the use? Probably only freeing that object sooner. And no matter really how it came there (C++ or other), that was probably the *intention* of the initial developper.
Wernight
@Ben: Setting smart pointers to null is not necessary **except when it is necessary**. Some COM objects have ordering requirements on their release; for example, you're not allowed to do the final release on a structured file storage if you still have a reference outstanding on a child stream. I've frequently written code where I was required to tear down objects in a precise order. Now, in those cases I personally prefer to use dumb pointers; when the pointers are smarter than I am, I use them wrong. But it certainly is the case that sometimes you want to do this explicitly for good reason.
Eric Lippert
@Wernight, I'm sorry, was focusing only on guiding the questioner to the right answer. I responded to your pre-edit answer, about it being equivalent to Dispose.
Dykam
@Wernight: You said it was required. How's that different?
Ben Voigt
@Eric: You can always destroy the pointer, so it is never necessary to assign NULL to it.
Ben Voigt
@Ben: Indeed smart pointers don't *require* setting them to null except to break a cyclic reference. I should have insisted on the fact that it was probably a habit to possibly free memory earlier, or a scared developper "playing safe".
Wernight
+1  A: 

Consider a slight modification:

public void FooBar() 
{ 
    object foo = null; 
    object bar = null; 

    try 
    { 
       foo = new object(); 
       bar = new object(); 

       // Code which throws exception. 
    } 
    finally 
    { 
       // Destroying objects 
       foo = null; 
       bar = null; 
    } 
    vaboom(foo,bar);
} 

The author(s) may have wanted to ensure that the great Vaboom (*) did not get pointers to malformed objects if an exception was previously thrown and caught. Paranoia, resulting in defensive coding, is not necessarily a bad thing in this business.

(*) If you know who he is, you know.

John R. Strohm
The finally block guarantees that the references are null, whether an exception occurs or not.
Ben Voigt
Even if none of the code following the 'finally' block would make use of invalid 'foo' or 'bar' objects, clearing them out in the 'finally' clause will ensure that they are eligible for garbage collection during the execution of 'vaboom'. In most cases, it wouldn't really matter whether the objects were gc'ed before or after 'vaboom', but if e.g. 'vaboom' contains the main loop of a socket-monitoring thread, killing 'foo' and 'bar' earlier may be useful.
supercat
+7  A: 

It is possible that it came from VB which used a reference counting strategy for memory management and object lifetime. Setting a reference to Nothing (equivalent to null) would decrement the reference count. Once that count became zero then the object was destroyed synchronously. The count would be decremented automatically upon leaving the scope of a method so even in VB this technique was mostly useless, however there were special situations where you would want to greedily destroy an object as illustrated by the following code.

Public Sub Main()
  Dim big As Variant
  Set big = GetReallyBigObject()
  Call big.DoSomething
  Set big = Nothing
  Call TimeConsumingOperation
  Call ConsumeMoreMemory
End Sub

In the above code the object referenced by big would have lingered until the end without the call to Set big = Nothing. That may be undesirable if the other stuff in the method was a time consuming operation or generated more memory pressure.

Brian Gideon
I did some VB6 and VB4 programming back in the day, and the advice to set objects to null when you were done with them was EVERYWHERE, especially for database connections through ADO or DAO. Frankly, I think it is a case of there being some situation where it was called for and a lot of other developers adopting the practice by superstition rather than understanding the reason you might do that. That said, in VB6 I did find that there were times where I had to do this when two objects formed a circular reference. VB didn't always tear those constructs down correctly and could leak memory.
JohnFx
I agree. Probably originated back when people went from VB6 to VB.net. Then the copy/paste .net coders carried the myth into production systems.
dotjoe
+2  A: 

I suspect that this pattern comes from translating C++ code to C# without pausing to understand the differences between C# finalization and C++ finalization. In C++ I often null things out in the destructor, either for debugging purposes (so that you can see in the debugger that the reference is no longer valid) or, rarely, because I want a smart object to be released. (If that's the meaning I'd rather call Release on it and make the meaning of the code crystal-clear to the maintainers.) As you note, this is pretty much senseless in C#.

You see this pattern in VB/VBScript all the time too, for different reasons. I mused a bit about what might cause that here:

http://blogs.msdn.com/b/ericlippert/archive/2004/04/28/122259.aspx

Eric Lippert
A: 

I've seen this in some Java code before. It was used on a static variable to signal that the object should be destroyed.

It probably didn't originate from Java though, as using it for anything other than a static variable would also not make sense in Java.

Jonathan Sternberg