[Mono-devel-list] Re: [Gtk-sharp-list] Disposing problem

Jonathan Pryor jonpryor at vt.edu
Sun Nov 9 18:31:05 EST 2003


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




More information about the Mono-devel-list mailing list