36

I struck up a conversation with my colleague today, who said she'd just learned the reason behind using the using statement.

 //Using keyword is used to clean up resources that require disposal (IDisposable interface). 

 using (StreamReader reader = new StreamReader(@"C:\test.txt")) 
 { 
 string line = reader.ReadLine(); 
 } 

I pointed out that the object is marked as "Can be disposed" but not actually disposed and garbage-collected unless GC decides to do so.

She responded that the object will be disposed automatically once that using statement ends because the using statement is translated to try-catch-finally block. So, the object must get disposed at the very end of the using statement.

I was confused by this, because I know that using a using statement does not guarantee that the object gets GC-collected. All that happens is that the Dispose() method would be called. The GC decides when to GC it regardless. But when she asked for proof, I could not find any.

Does anyone know how this works, and how to prove it?

doppelgreener
  • 4,809
  • 10
  • 46
  • 63

2 Answers2

43

You're talking about two very different things.

The object will be disposed as soon as the using-block ends. That doesn't say anything about when it is garbage collected. The only time heap memory is released is when a garbage collection occurs - which only happens under memory pressure (unless you use GC.Collect explicitly).

Disposing an object simply means calling its Dispose method. That most often means releasing either a scarce resource, or a native resource (in practice, all scarce resources are native - sockets, files, ...). Now, it's handy that the lifetime of the disposable object in your case is limited in scope, so it could theoretically be collected as soon as the using-block ends - however, that doesn't really happen in practice, since the .NET runtime tries to avoid collections - they're expensive. So until you cross a memory allocation threshold, no collection is going to happen, even though you have a dead object on the heap.

So what's the point of Dispose? Nothing to do with managed memory. You don't really care about managed memory, and you shouldn't expect that Dispose will actually be called - it doesn't have to be. The only thing that has to be called by the runtime is the finalizer, and you can only ever use that for disposing of native resources - in fact, there's no guarantee if the objects you have a reference to still exist by the time the finalizer runs - the managed memory might have already been reclaimed by then. That's why you never handle managed resources in a finalizer.

So yes, she was completely right. The point is that IDisposable has nothing to do with the garbage collector. Disposed does not mean garbage collected.

Luaan
  • 62,244
  • 7
  • 97
  • 116
  • 1
    Also, you can read more about it on this post: http://stackoverflow.com/questions/11927827/using-statement-does-it-trigger-garbage-collection – Zohar Peled Apr 22 '14 at 07:40
  • 1
    This answer is mostly correct, but there's rather too much emphasis on saying that the disposable pattern is unrelated to garbage collection. This is only true for objects that aren't finalizable. Any object with a finalizer will be treated differently by GC, and if the object is not disposed (including GC.SuppressFinalize) it may stay around for a lot longer before its memory is reclaimed. – Dominic Cronin Apr 22 '14 at 11:10
  • @DominicCronin True, but that's just the shortcut - it's better to release native resources sooner. And indeed, the garbage collector has no knowledge of your unmanaged memory; the finalizer is there exactly because of that - it allows you to safely handle unmanaged memory as well, by concentrating the necessary finalization code inside something that will be called at some point - and as an optimalization, can be suppressed - which is usually done in the Dispose method. That's why I'm saying that GC has nothing to do with `IDisposable` - GC doesn't care, you have to care. – Luaan Apr 22 '14 at 11:21
  • The runtime doesn't *have* to call your finalizer in some situations, so it isn't a good idea to use it for anything with semantic importance. Obviously sudden process termination doesn't count, but one misbehaving finalizer can cause the runtime to just give up while it's trying to shut down and not call anything else. During runtime you do have an assurance that finalizers will be called if there's actually a collection, though. – Phoshi Apr 22 '14 at 15:03
  • @Phoshi Yeah, but when your process gets killed, all of its native resources are released as well, simply because they're tied to your process. So the usual cause of misbehaving shutdowns is when two or more applications use native memory between them and handle it wrong. There's things you can rely on (files will be closed) and things you can't (your own `Dispose` code that e.g. deletes a temporary file on the disk). – Luaan Apr 22 '14 at 15:04
  • 1
    @Luaan: Absolutely, yeah, you don't need to worry about a large set of issues, which is why I specified semantic importance. I don't really consider releasing file handles to have any useful semantics, because whether it happens or not shouldn't change your behaviour. – Phoshi Apr 22 '14 at 15:06
  • @Luuan - the GC is not "aware" of Dispose, but implementing and using IDisposable will affect the way the GC runs. An object with a finalizer will always survive its first GC, because it will remain freachable. If Dispose has already done its work, and called SuppressFinalize, this won't happen. – Dominic Cronin Apr 22 '14 at 18:26
  • @DominicCronin Well, that has nothing to with `IDisposable` - it's just the recommended pattern for disposing of native resources. If I name that method `ReleaseResources`, it will work exactly the same. Also, note that while the object itself will live, its references are no longer considered live, and by the time the finalizer runs, they could have already been reclaimed - so the only memory you're still holding on to is the native memory and the fields of your object - if you've got enough finalizers to make this a significant part of your memory usage, you're doing something very wrong :) – Luaan Apr 23 '14 at 06:57
  • @Luuan - The Disposable pattern is firmly established, and there is clear advice from Microsoft on how it is to be implemented. This includes calling SuppressFinalize, so there's a direct relationship with the GC. If we're to be pedantic, well then I could define my own IDisposable, but this is the real world and conventions are important. IDisposable *does* have a relationship with garbage collection. Insisting otherwise isn't helping people who come here to learn good ways of working. – Dominic Cronin Apr 27 '14 at 17:48
  • @DominicCronin Yes, there is advice. And yes, in many, many BCL classes, the disposable pattern is not implemented at all, even though the classes do "implement" `IDisposable`. You're still mixing up two things - `IDisposable` with a finalizer, and `IDisposable` without a finalizer. Obviously, `SuppressFinalize` is *only* used when `this` has native resources and a finalizer. It's *not* used when you're not using native resources directly (i.e. when you're using `SafeHandle`, `Socket` etc.) - while `IDisposable` itself still makes sense (finalizer is about "certainty" - disposable about time). – Luaan Apr 28 '14 at 08:49
  • @luuan - I refer you to my first comment in this thread, where I said exactly that. My point was that anyone who thinks the disposable pattern is unrelated to GC has missed some relevant facets of the discussion. Specifically, that when objects *are* finalizable, failing to implement the pattern correctly can result in them unnecessarily surviving collections. So the two things *are* related - in the context of a question about what a using block does. – Dominic Cronin Apr 28 '14 at 11:09
12

She is right on the money using statement is just a syntatic sugar for try/finally{obj.Dispose();} . using statement ensures that the object will be disposed.(Dispose method will be called) but it has no relationship with garbage collection.

Take a look at this Understanding-the-using-statement

Short answer: So now we know using statement is just calling Dispose and does nothing other than that, and keep in mind that Dispose method is no special than any other methods. It is just a method and that's it. So it is no way related to garbage collection. Interestingly "Garbage Collector" doesn't even know about Dispose method or IDisposable.

Hope this helps

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • so the `object` will be `null` but still remains on the `heap` unless GC GC's it. correct ? – now he who must not be named. Apr 22 '14 at 07:34
  • 3
    object will not be go `null` unless you explicitly set it to `null`. object will live in memory if you have live reference to the object somewhere despite the fact that object is disposed. – Sriram Sakthivel Apr 22 '14 at 07:35
  • @SriramSakthivel And most importantly, for local variables, it makes no difference whether you set it to `null` or not - the runtime is smart enough to figure out you're not going to use the reference. However, if you do, it will *not* be null. – Luaan Apr 22 '14 at 07:38
  • @Luaan I carefully framed my statement saying that if you have "live reference" it won't be GC'd. It doesn't matter local variable, instance variable or whatever – Sriram Sakthivel Apr 22 '14 at 07:40
  • @SriramSakthivel Indeed, I was just pointing out that local variables are not guaranteed to last as long as the method they're declared in - they're only guaranteed to last as long as you need to. In other words, the "liveness" of the reference is not tied to the method ending, even if you don't explicitly set it to `null`. – Luaan Apr 22 '14 at 07:43
  • 1
    @Luaan You're right indeed, but that's different story. That goes deep to the jit optimizations and GC in depth. To keep it simple and not to be pedantic I put this way :) – Sriram Sakthivel Apr 22 '14 at 07:46
  • 3
    +1 `"Garbage Collector" doesn't even know about Dispose method or IDisposable` – Sudhakar Tillapudi Apr 22 '14 at 07:57
  • "She is right on the money using statement is just a syntatic sugar for try/finally." What? No it's not. Unless you assume that dispose() gets called in every finally block. – Phil Sandler Apr 22 '14 at 12:28
  • @PhilSandler Can you point one instance where using statement won't run finally block (except `OOM`,`StackOverflowException`,`ThreadAbortException`)? – Sriram Sakthivel Apr 22 '14 at 12:30
  • I don't understand your question. It's syntatic sugar for Dispose(), not for try/finally. Try/finally is useful in scenarios having nothing to do with disposal. The two concepts are orthogonal to each other. – Phil Sandler Apr 22 '14 at 12:39
  • 3
    @PhilSandler Yes I worded it wrong, it is syntatic sugar for `try...finally{Dispose()}`. Does that makes sense? – Sriram Sakthivel Apr 22 '14 at 12:41
  • Yep, fair enough. :) – Phil Sandler Apr 22 '14 at 12:43