[Gtk-sharp-list] Disposing problem

Bruno Fernandez-Ruiz brunofr@olympum.com
Mon, 10 Nov 2003 08:38:54 +0200


Thanks a lot Jon for a clear explanation! I had been wondering for a
long time about the finalization problem. You are right, in Java the
finalization is not deterministic, but the collection is, and that's
probably the main difference with the Bohem GC. I did not know about
per-generation finalization in .NET, so I think I have some reading to
do :)

Bruno

On Mon, 2003-11-10 at 01:31, Jonathan Pryor wrote:
> There's the .NET/CIL interpretation, and then there's what Mono does.
> 
> For most of this, they actually agree. :-)
> 
> Unlike Java, .NET/CIL/Mono (eventually) will make an effort to run
> object finalizers (Object.Finalize).  However, finalizers are run
> specially: when a "dead" (unreferenced) object is found, the finalizer
> isn't run immediately; instead, the object is placed on a finalization
> queue, and during the NEXT GC the objects on the finalization queue are
> executed.  Or, the objects in the finalization queue are run on a
> separate thread.  Whatever.  Also note that the finalization queue is
> per-generation, so if it's a Gen-2 object that was placed on the
> finalization queue, it may not be until app-exit that the finalizer is
> run.
> 
> Problem #1 is that everything that derives from GLib.Object has a
> finalizer, and thus has the above deferred behavior with the GC system.
> 
> This, however, isn't the major problem.
> 
> The major problem is Problem #2 (as far as you're concerned), which is
> that the gtk-sharp library is keeping a reference to all GLib objects. 
> GLib.Object.PerformQueuedUnrefs is fully-managed code.
> 
> So, your understanding is correct, it's just that another library
> (gtk-sharp) is keeping a reference to your objects (Pixbufs) without you
> realizing it.  This is handled by the GLib.Object constructor & related
> methods, so *every* GLib.Object-derived class will exhibit this
> behavior.
> 
> To get gtk-sharp to remove it's reference to your objects, you need to
> let it run GLib.Object.PerformQueuedUnrefs, which requires running the
> idle handler.
> 
> Due to Problem #1, there will be a delay before the memory is generally
> usable.  First you need to perform the queued unrefs, then the GC will
> realize that the objects are unreachable, and (if GC.SuppressFinalize
> hasn't been called on the object), the objects will be placed on the
> finalization queue and collected on the NEXT GC (again, for that GC
> generation).
> 
> Note that invoking Dispose() on the objects will call
> GC.SuppressFinalize, so calling Dispose will remove the need to wait for
> the 2nd GC to collect memory.
> 
> Where mono differs from the above is that the Boehem GC is used, and
> things are still in flux, so it is less likely for finalizers to be
> invoked.  This will likely change (if it hasn't been fixed already), as
> we get a better GC infrastructure.
> 
>  - Jon
> 
> On Sun, 2003-11-09 at 13:57, Bruno Fernandez-Ruiz wrote:
> > I understand from your message that under Gtk# managed objects are
> > prevented to be GC'ed, and queued to be dereferenced in the unmanaged
> > code. Your code should work as it allows idle cycles to happen
> > (yielding). I will try it out on my Gtk# application and let you know if
> > it works.
> > 
> > Now, I still have a question beyond Gtk#, and more related to the Mono
> > runtime. 
> > 
> > Please accept my apologies if I am getting it wrong, but I assimilate
> > the mono VM to a JVM, where both GUI and non GUI managed applications
> > run on a managed memory heap. Roughly speaking, on a JVM, if after a
> > marking cycle from the GC thread, there is not enough marked objects to
> > be reclaimed to leave enough free memory for a requested allocation, the
> > actual heap is expanded up. If the max heap is reached, and there is
> > still more memory requested, then the application crashes with an out of
> > memory error. 
> > 
> > In my sample using mono, I would expect the VM GC mark policy, whichever
> > policy is used, to limit the amount of memory used, as the test objects
> > are not kept referenced, at least by the application code. So either,
> > the GC mark and sweep is not happening, or something else is keeping a
> > reference to the objects that prevents finalization. Instead, CPU sky
> > rockets, and the system starts swapping memory until freeze.
> > 
> > Could somebody please give me some light? My experience from heavily
> > loaded server-side applications, enterprise level, ist that optimized GC
> > is critical for system performance.
> > 
> > Thanks!
> > Bruno
> > 
> > public class Test
> > {
> >         ~Test ()
> >         {
> >                 Console.WriteLine ("Finalized");
> >         }
> >                                                                                                                                                                                    
> >         public static void Main ()
> >         {
> >                 for (long i = 10000000000; i > 0; i--)
> >                         new Test ();
> >         }
> > }
> > 
> > 
> > On Sun, 2003-11-09 at 18:32, Jonathan Pryor wrote:
> > > The problem is that you're not a GUI app. :-)
> > > 
> > > PerformQueuedUnrefs is called from the Idle handler for the GUI.  The
> > > Idle handler is run whenever the GUI is not busy.
> > > 
> > > (Recall that GUIs are event-driven, so if the user isn't doing anything,
> > > and the app isn't doing anything, then the app is idle, so you can do
> > > background work during idle processing.)
> > > 
> > > Your sample app doesn't have a GUI, so there's never an "idle time", so
> > > the idle handler is never run, so PerfrmQueuedUnrefs is never called.
> > > 
> > > Some possible solutions:
> > >   - Insert the code:
> > > 	while(GLib.MainContext.Iteration()) {
> > > 		// do nothing
> > > 	}
> > >     This should explicitly run the idle handler.  This should be run
> > >     after the .Dispose() call.
> > > 
> > >   - Make Object.Dispose virtual, then make Pixbuf.Dispose free memory 
> > >     immediately.
> > > 
> > >     This probably isn't a good idea, as GTK+ requires that all objects
> > >     be disposed from the same thread, which is what PerformQueuedUnrefs
> > >     does (since the idle handler is only run on the GUI thread, so only
> > >     one thread will ever do the unrefs).
> > > 
> > >     Pixbuf might not have this requirement, though.  I have no idea.
> > > 
> > > The first option is the safest and simplest option, if it works.  Please
> > > try it and let us know.
> > > 
> > >  - Jon
> > > 
> > > On Sat, 2003-11-08 at 13:55, Bruno Fernandez-Ruiz wrote:
> > > > On Sat, 2003-11-08 at 20:54, Gonçal Carrero Puig wrote:
> > > > > Anyone knows why idle is never calling PerformedQueuedUnrefs?
> > > > 
> > > > I have seen similar problems in due to the GC thread never entering, and
> > > > the heap expanding until memory exhaustion. Try to create many dummy
> > > > objects that implement IDisposable, and see if they get reclaimed. When
> > > > running the attached sample, I freeze the system and run out of memory.
> > > > No collection ever happens. 
> > > > 
> > > > Is GC disabled by default in mono?
> > > > 
> > > > Bruno
-- 
Bruno Fernandez-Ruiz <brunofr@olympum.com>
The Olympum Group,  http://www.olympum.com