From: David Boucherie & Co on 26 Jun 2010 15:13 Andrew Baker wrote: > Because the timer is active (enabled), there's still a live reference to > it, preventing the garbage collection. You've not got direct access to > this reference, 'the system' has got one inside the workings of the .net > framework somewhere. That doesn't seem to be it. I tried Daniel's code with an int instead of a Timer, and still no joy. I can't explain it... See my answer before, about the workings of the garbage collector... I thought I understood the thing pretty well. It seems I was delusional... :P using System; namespace ConsoleApplication1 { public class SomeClass : IDisposable { int someHandle; public SomeClass() { someHandle = 1; } ~SomeClass() { Console.WriteLine( "Finalizing!" ); } public static void Main() { { SomeClass someObject = new SomeClass(); someObject.Dispose(); } Console.WriteLine( "Collection 1." ); GC.Collect( 2, GCCollectionMode.Forced ); Console.WriteLine( "Suspending thread waiting for finalizers." ); GC.WaitForPendingFinalizers(); Console.WriteLine( "Collection 2." ); GC.Collect( 2, GCCollectionMode.Forced ); Console.ReadLine(); } #region IDisposable Members public void Dispose() { Console.WriteLine( "Disposing the handle." ); someHandle = 0; } #endregion } }
From: David Boucherie & Co on 26 Jun 2010 15:26 Addendum: It may be a good idea when implementing Dispose() to finalize your object yourself and tell the garbage collector that there is no need for it to finalize your object. I didn't do that in my previous code, because I wanted to see when the finalization fired. In the code I sent before, write your finalization code in your Dispose() method and leave the ~TimerTest() out. public void Dispose() { Console.WriteLine( "Disposing timer." ); timer.Dispose(); GC.SuppressFinalize( this ); } Since you have to Dispose() anyway, you can as well finalize yourself. This gives as added bonus that you have control over when "finalization" takes place.
From: Peter Duniho on 27 Jun 2010 03:54 David Boucherie & Co wrote: > Addendum: > > It may be a good idea when implementing Dispose() to finalize your > object yourself and tell the garbage collector that there is no need for > it to finalize your object. Technically "finalize" refers _only_ to the execution of a finalizer. One generally only ever lets the CLR call the finalizer. You don't call it yourself. > I didn't do that in my previous code, because I wanted to see when the > finalization fired. > > In the code I sent before, write your finalization code in your > Dispose() method and leave the ~TimerTest() out. > > public void Dispose() > { > Console.WriteLine( "Disposing timer." ); > timer.Dispose(); > GC.SuppressFinalize( this ); > } > > Since you have to Dispose() anyway, you can as well finalize yourself. > This gives as added bonus that you have control over when "finalization" > takes place. The proper convention looks something like this: class A : IDisposable { private IDisposable _managedDisposable; private IntPtr _unmanagedResource; public void Dispose() { Dispose(true); } protected virtual void Dispose(bool disposing) { SomeFunctionThatFreesTheIntPtrResource(_unmanagedResource); if (disposing) { _managedDisposable.Dispose(); GC.SuppressFinalize(this); } } ~A() { Dispose(false); } } Note that when the virtual Dispose(bool) method is being called by the finalizer, the managed IDisposable object is ignored. This is because generally, if the containing object is being finalized, then the disposable objects it contains are also going to be finalized (assuming no one else has any reference to them�which they shouldn't if there's any chance your object is going to dispose it!). There's no point in your own Dispose(bool) method trying to dispose the object; you've got 50/50 odds it's already been disposed anyway by the time your finalizer runs, and it will be for sure if it can be, even without your own object disposing it. Pete
From: Peter Duniho on 27 Jun 2010 03:59 David Boucherie & Co wrote: > [...] > Allocated objects (in the managed heap) get a generation: > > � 0: very-short lived objects (like temporary variables). Careful with the terminology. A variable in and of itself isn't an object, nor subject to collection in and of itself (though of course it could be part of a heap-bound object). Perhaps you mean "like objects referred to by temporary variables"? Except that just because an object is referred to by a temporary variable (whatever one might mean by that), that doesn't mean it's not reachable by other means. So maybe instead you simply mean "like temporary objects". But I would say that by definition, any "very-short lived object" is a "temporary". So I guess I don't really know what was intended in the parenthetical statement. > [...] > A full garbage collection has three phases: > > � Live objects are marked, dead objects become "condemned". > � References to objects that will be compacted (moved together, simply > put) are updated. > � Space occupied by dead objects is reclaimed and surviving objects are > compacted. However, dead objects that have finalizers are not reclaimed, > but marked as finalize pending. Also, all references in the finalizers > cause the objects referenced to be kept alive too, even if they would > otherwise be condemned! That last statement is either incorrect, or you are being sloppy with your terminology. Specifically: you describe objects in the finalization queue as "dead objects that have finalizers", but then describe other objects they reference as "alive". Except that any objects reachable _only_ through an object that is about to be finalized (i.e. already marked as "dead" and in the finalization queue) have themselves already been marked as "dead" as well and if they have finalizers, will themselves be in the finalization queue. That's why it's so important to not attempt to use disposable/finalizable objects from your own finalizer. They may have actually been finalized before your own object. Now, in reality, I don't think the word "dead" is really accurate here. After all, an object isn't truly dead until the memory it was using has been reclaimed and is no longer available as that object. And as you point out, an object with a finalizer doesn't have its memory reclaimed until it's been finalized. So there's not really any such thing as a "dead object that has a finalizer". A truly dead object doesn't have anything at all, no memory, no finalizer, no nothing. > [...] > In short: when you run your garbage collector, there is no way to be > sure that a certain object will be effectively destroyed at any one > time, nor can you exactly predict when finalizers will be run. > > To force a full garbage collection (generation 2), use: > GC.Collect(2, GCCollectionMode.Forced ); The parameterless overload of GC.Collect() is the same as calling GC.Collect(2, GCCollectionMode.Force). As for forcing collection and finalization, the code original posted does exactly that. It calls GC.Collect(), which forces an immediate collection of all generations, and then it calls GC.WaitForPendingFinalizers(), which does not return until all the objects in the finalization queue have been finalized. > Now that I have explained all that... I have NO CLUE why your timer > doesn't get killed. :P It doesn't even finalize after disposing it, even > when forcing a full garbage collection! The finalizer only runs when the > program ends (after ReadLine()). > I suppose some hidden references exist... but I don't see where. I even > tried with a simple class, replacing your timer with an int instead, and > still no joy. > > Anyone can explain it? Because I can't! Did you try compiling the code as an optimized (i.e. "Release") build? As I mentioned in a previous post, the JIT compiler is not as aggressive in unoptimized builds, and local variables hold their references until the very end of the method. Only in optimized builds is it possible that the JIT compiler will shorten the lifetime of an object's reference within a method. > On a side note: > Your code contains a resource leak. > Your Timer will eventually be collected, but as you don't Dispose() it, > it will keep its system resources located. Disposable objects are supposed to have finalizers, and the System.Timers.Timer object does in fact meet this requirement. As such, it will eventually free whatever resources would normally have been freed when the Dispose() method is called. Besides, it doesn't even make sense to say that an object "will eventually be collected" but then also say that "it will keep its system resources [al]located". An object that has been collected can't possibly _keep_ anything allocated. If it's been implmented poorly, there might be a bug in which it fails to free some resources, but that's highly unlikely in this case, given the presence of a finalizer. > I changed your code a bit, and to my astonishment, the Timer isn't > collected... There's nothing in the version you posted that would make the object any more likely to be collected. Eligibility for collection has _nothing_ to do with whether an object has been disposed of or not, nor whether GC.SuppressFinalize() has been called. It's entirely about whether the object is reachable or not. In your version, all you have done is wrap the reference to the Timer in another object. But just as the Timer reference remained reachable in the original code, so too does the reference to your own object, and thus so too does the Timer reference contained within. So for the same reason that the Timer isn't collected, your own object is also not collected and so the Timer again remains uncollected. Pete
From: Peter Duniho on 27 Jun 2010 05:05 Daniel Lidstrom wrote: > [...] > Can someone please explain why the Elapsed event is still firing after > the first ReadLine()? Thanks in advance! I finally had some time (well, if I stay up late :) ) to try your code example. In spite of being able to force other objects to be collected (e.g. the TimerTest instance that is created), the Timer object is not collected. The only logical explanation is that the documentation is out-of-date (or was never correct) and the Timer class implementation preserves a reference to the object internally to .NET somewhere, even if your own program does not, thus preventing it from being collected. See below for a variation on the original code example that shows that the TimerTest object is collected. It is implausible that it would be collected but the Timer object would not be, unless the Timer object really does still remain reachable via some other means (e.g. an internal data structure in .NET). Reminder: it is very important when running a test like this to run an optimized build, and to _not_ run the program using the debugger (i.e. just open a command prompt window and run it from there). An unoptimized build or execution from the debugger will inhibit the GC/JIT compiler behavior that we are trying to see. Pete using System.Timers; using System; public class TimerTest { public TimerTest() { var timer = new Timer(1000); timer.Elapsed += (o, e) => { Console.WriteLine("Timer elapsed"); }; timer.Start(); } public static void Main() { var timerTest = new TimerTest(); WeakReference weakLocal = new WeakReference(timerTest); Console.WriteLine("press Enter to collect..."); Console.ReadLine(); GC.Collect(); GC.WaitForPendingFinalizers(); Console.WriteLine("collected; weakLocal is alive: " + weakLocal.IsAlive); Console.ReadLine(); } }
First
|
Prev
|
Next
|
Last
Pages: 1 2 3 4 Prev: PDF font features Next: Real Programmers (TM) use MSFT C# not Linux languages (sez an expert) |