GTK+ Forums

Discussion forum for GTK+ and Programming. Ask questions, troubleshoot problems, view and post example code, or express your opinions.
It is currently Tue Sep 02, 2014 9:23 pm

All times are UTC




Post new topic This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 
Author Message
 Post subject: Gtk memory management in a nutshell
PostPosted: Thu Dec 25, 2008 4:05 pm 
Offline
Never Seen the Sunlight

Joined: Wed Jul 23, 2008 10:31 am
Posts: 2406
Location: Slovenia
11 May 2009: Update
    GtkTree(List)Store manipulation section has been updated a bit to better explain handling of boxed types

8 december 2009: Update
    Reference counting is now explained from regular, consumer developer point of view


Hello.

Lately, there has been quite a few question asked about memory management on this forums and gtk mailing list, so I decided to sum up that I've learned while using gtk. Here we go.

Reference counting

We know that all Gtk objects are directly or indirectly derived from GObject. One of the fundamental functionalities of GObject is (among others) reference counting, which helps programmer with memory management.

When the GObject is created, it has reference count of 1. This reference count can be increased or decreased with g_object_ref and g_object_unref functions respectively. When GObject's reference count drops to zero, it destroys itself and frees memory. But as always, there are some exceptions which will be discussed later.

Let's have a look at the life cycles of different objects. This is the code I'll be dissecting:
Code:
#include <gtk/gtk.h>

enum
{
   COL_TEXT,
   COL_IMG,
   N_COLS
};

int
main( int    argc,
     char **argv )
{
   GtkWidget         *window;
   GtkWidget         *treeview;
   GtkListStore      *store;
   GtkTreeIter        iter;
   GtkTreeViewColumn *column;
   GtkCellRenderer   *renderer;
   
   register gint      i;
   
   
   gtk_init( &argc, &argv );
   
   /* Create main window */
   window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
   gtk_window_set_default_size( GTK_WINDOW( window ), 200, 150 );
   g_signal_connect( G_OBJECT( window ), "destroy",
                 G_CALLBACK( gtk_main_quit ), NULL );
   
   /* Create tree view and pack it into window */
   treeview = gtk_tree_view_new();
   gtk_container_add( GTK_CONTAINER( window ), treeview );
   
   /* Create store and populate it with some data */
   store = gtk_list_store_new( N_COLS, G_TYPE_STRING, GDK_TYPE_PIXBUF );
   for( i = 0; i < 5; i++ )
   {
      GdkPixbuf *pixbuf;
      
      pixbuf = gtk_widget_render_icon( GTK_WIDGET( treeview ),
                               GTK_STOCK_CANCEL,GTK_ICON_SIZE_BUTTON,
                               NULL );
      
      gtk_list_store_append( store, &iter );
      gtk_list_store_set( store, &iter,
                     COL_TEXT, g_strdup_printf( "Sample %d", i + 1 ),
                     COL_IMG, pixbuf,
                     -1 );
      
      g_object_unref( G_OBJECT( pixbuf ) );
   }
   
   /* Set treeview's model */
   gtk_tree_view_set_model( GTK_TREE_VIEW( treeview ),
                      GTK_TREE_MODEL( store ) );
   g_object_unref( G_OBJECT( store ) );
   
   /* Create display part of tree view - column 1 */
   renderer = gtk_cell_renderer_text_new();
   column = gtk_tree_view_column_new();
   gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( column ), renderer, TRUE );
   gtk_tree_view_column_set_attributes( column, renderer,
                               "text", COL_TEXT, NULL );
   gtk_tree_view_append_column( GTK_TREE_VIEW( treeview ), column );
   
   /* Create display part of tree view - column 2 */
   renderer = gtk_cell_renderer_pixbuf_new();
   column = gtk_tree_view_column_new();
   gtk_cell_layout_pack_start( GTK_CELL_LAYOUT( column ), renderer, TRUE );
   gtk_tree_view_column_set_attributes( column, renderer,
                               "pixbuf", COL_IMG, NULL );
   gtk_tree_view_append_column( GTK_TREE_VIEW( treeview ), column );
   
   gtk_widget_show_all( window );
   
   gtk_main();
   
   return( 0 );
}

Direct descendants of GObject

The first object we'll observe is GtkListStore, since it is direct descendant of GObject.

When we create it with gtk_list_store_new, it has reference count of 1. We own this reference, since we were the one who created this object. When we assign it to the tree view with gtk_tree_view_set_model, tree view, tree view will also own list store. After assigning store to tree view, we don't need store anymore, so we remove our reference from store using g_object_unref call (in some gtk tutorials/books this is described as "removing your own reference", since store is now owned solely by tree view). And when we quit our program, before tree view gets destroyed, it releases store which gets destroyed together with tree view.

This was the typical GObject life cycle. Now let's have a look at GtkWidget widget.

Descendants of GInitiallyUnowned

Looking at the GtkWidget hierarchy, we'll find that one of the ancestors is GInitiallyUnowned. And this introduces a little twist in reference counting. When we create an object that inherits from GInitiallyUnowned, that object has reference count of 0, but has a floating reference. This is where calls name comes from: you don't own an initial reference on newly created object.

In order to put the things to their proper place, this floating reference needs to be sinked with g_object_ref_sink, where sinking means converting floating reference to normal reference. After the sinking is done, these kind of objects behave exactly the same as normal GObjects.

So, our treeview's life goes through these phases: gtk_tree_view_new() creates new object with floating reference (no one owns this reference); gtk_container_add call adds our treeview into window which is now the owner of the tree view. When the program quits, toplevel window releases treeview (which gets destroyed).

The same principle applies to GtkTreeViewColumn and GtkCellRenderer.

What about our toplevel window? It is descendant of GInitiallyUnowned after all. GtkWindow has a special ability to sink it's own floating reference, and when we call gtk_window_new, we get a window with a reference count of 1 and no floating reference. Or said otherwise, GtkWindow behaves like GObject, despite the fact that it's derived from GInitiallyUnowned.

Strings

Glib library offers quite a few useful functions to manipulate strings. They are usually much easier to use than libc counterparts because they allocate memory for returned string for you. But you mustn't forget to free it after the resulting strings are not needed anymore. In short: if API documentation says that the function returns newly allocated string, you must free it with g_free.

This section was short, but it is important to remember this for the next section.

GtkTree(List)Store data and object properties

Operations related to storing data in stores and retrieving it can lead to quite large memory leaks, since these operations tend to be executed in loops. Operations on object properties are less problematic, but it still pays off to do them right.

GtkTree(List)Store manipulation

When populating store with gtk_tree_store_set, the data that we pass in is handled differently according to it's type:
  • if the data passed in is object, then store takes ownership of it
  • simple data types like integers and pointers are simply copied to the store (Important: when storing a pointer, only pointer is stored inside data store, data pointed to by the pointer is not copied!!)
  • boxed types like strings and GdkColors are first copied and pointer to the copied data is stored inside data store
In example code, text gets copied and then pointer to that copied text is stored in first column of list store. Pixbuf on the other hand is not copied, it's reference count is increased by 1 instead and the pointer to pixbuf is stored in second column of our list store.

We also need to be careful when retrieving data from store. Again, there is difference when retrieving different types of data:
  • when the object is retrieved, it's reference count is increased by 1 (we own one reference in this object)
  • simple types are copied before handed to us
  • boxed types are also coped before a pointer to data is handed to us
The main consequences of that are that we need to call g_object_unref on objects that we don't need anymore or free the data we retrieved from store if appropriate (you'll want to free strings and other boxed types when you don't need them any more). Note: boxed types should be freed by the appropriate function. Generic way of doing it is to call g_boxed_free on boxed type.

Manipulating object's parameters

The exactly the same principles apply as for the tree view store manipulation.

Test

My sample code suffers from memory leak. Can you find?

That's it. I hope you learned something new.

You are highly encouraged to post constructive comments, suggestions and of course corrections;-)

References
Glib
GObject
Gtk


Last edited by tadeboro on Tue Dec 08, 2009 7:01 pm, edited 3 times in total.

Top
 Profile  
 
 Post subject: Re: Gtk memory management in a nutshell
PostPosted: Fri Jan 02, 2009 10:30 pm 
Offline
GTK+ Geek

Joined: Wed Dec 19, 2007 9:15 pm
Posts: 61
Location: Glasgow, Scotland
tadeboro wrote:
My sample code suffers from memory leak. Can you find?

I think it's the use of g_strdup_printf in gtk_tree_model_set. The text is copied, but the original is not freed.

_________________
Jabber – bcowan [at] fastmail.co.uk.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 12, 2009 4:20 am 
Offline
GTK+ Guru

Joined: Thu Jun 21, 2007 1:52 pm
Posts: 198
Location: Wilkes Barre Pa
Quote:
My sample code suffers from memory leak. Can you find?

Looks like the leak is on line 47.
Compiled with debug symbols:
gcc -g -o simple1 simple1.c `pkg-config --libs --cflags gtk+-2.0`

Ran valgrind:
valgrind --tool=memcheck --leak-check=yes ./simple1

Found proof of the leak:
malloc/free: 199,796 allocs, 158,155 frees,

Leak found here:
Quote:
==20011== 45 bytes in 5 blocks are definitely lost in loss record 57 of 191
==20011== at 0x4025D2E: malloc (vg_replace_malloc.c:207)
==20011== by 0x48C3157: __vasprintf_chk (in /lib/tls/i686/cmov/libc-2.8.90.so)
==20011== by 0x47822BE: g_vasprintf (in /usr/lib/libglib-2.0.so.0.1800.2)
==20011== by 0x476E125: g_strdup_printf (in /usr/lib/libglib-2.0.so.0.1800.2)
==20011== by 0x8048F4A: main (simple1.c:47)


Top
 Profile  
 
 Post subject: Re: Gtk memory management in a nutshell
PostPosted: Fri Jul 01, 2011 5:07 am 
Offline

Joined: Fri Jul 01, 2011 3:39 am
Posts: 1
I found out something interesting (related to this thread) that I wanted to share. In a nutshell I had a GObject that I wanted to have a floating reference instead of the default behavior of starting with an initial reference count of 1. I found that I could use g_object_force_floating and g_object_ref_sink to get the GObject behavior to be the same as a GtkWidget.

The details then...

I have a class that extends GObject (called Document). As posted in this thread the reference count of my object automatically went to 1. However, the problem that I ran into was that the object that I wanted to "own" the reference (called Page) was in a different part of the application. By own I mean I wanted the Page to g_object_ref() and g_object_unref() the Document. See, if the count was one when I created the Document, and I called g_object_ref on it inside the Page, I now have 2 references to it. So when the Page called g_object_unref on it then the memory is not freed up because the count is only done to 1.

The solution was to call g_object_force_floating() when the Document was created. Then when the Document was set on the page I used the g_object_ref_sink() to set the reference. By forcing the reference to be floating I was able to get the behavior to be the same as a GtkWidget.

Hope this helps someone else! This thread was a big help for me in understanding what I needed to do for my app.

-Jeff Johnston


Top
 Profile  
 
 Post subject: Re: Gtk memory management in a nutshell
PostPosted: Sat Jul 02, 2011 6:05 pm 
Offline
Never Seen the Sunlight

Joined: Wed Jul 23, 2008 10:31 am
Posts: 2406
Location: Slovenia
@jeff.johnston: Hello and welcome to the GTK+ forums.

You can simply derive your Document class from GInitiallyUnowned instead of plain GObject and get all the functionality you need for free. BTW, GtkWidgets are also derived from GInitiallyUnowned.

Cheers,
Tadej


Top
 Profile  
 
 Post subject: Re: Gtk memory management in a nutshell
PostPosted: Mon Sep 17, 2012 12:41 am 
Offline
GTK+ Guru

Joined: Fri Mar 25, 2011 5:16 pm
Posts: 177
Location: USA
The GObject library also provides the function g_object_is_floating() which checks whether an object has a floating reference and returns true if it does.
http://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#g-object-is-floating

Here's a small test program. It's Gtk+3.
Code:
/*  Compile with:
  gcc -Wall -o testrefence `pkg-config --cflags --libs gtk+-3.0` testrefence.c
*/

#include <gtk/gtk.h>
#include <glib/gprintf.h>

static gboolean is_floating (GtkWidget *object)
{
  gboolean floating;
  floating = g_object_is_floating (object);
  return(floating);   
}

int main(int argc, char *argv[])
{
  GtkWidget *window, *grid, *button;
 
  gtk_init (&argc, &argv);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  g_print ("window after gtk_window_new() : floating = %d\n\n", is_floating(window) );
  gtk_window_set_default_size (GTK_WINDOW(window), 120, 105);
  g_signal_connect (GTK_WIDGET (window), "destroy",
                    G_CALLBACK (gtk_main_quit), NULL);

  button = gtk_button_new_with_label ("BUTTON");
  g_print ("button after gtk_button_new_with_label : floating = %d\n\n", is_floating(button) );
  gtk_widget_set_size_request (GTK_WIDGET(button), 100, 75);

  grid = gtk_grid_new ();
  g_print ("grid after gtk_grid_new : floating = %d\n\n", is_floating(grid));
  gtk_grid_attach (GTK_GRID(grid),
                   GTK_WIDGET(button),
                   0, 0,
                   1, 1);
 
  g_print ("button after gtk_grid_attach : floating = %d\n\n", is_floating(button));
  g_print ("grid after gtk_grid_attach : floating = %d\n\n", is_floating(grid));

  gtk_widget_set_margin_top (GTK_WIDGET(grid), 20);
  gtk_widget_set_margin_bottom (GTK_WIDGET(grid), 20);
  gtk_widget_set_margin_left (GTK_WIDGET(grid), 25);
  gtk_widget_set_margin_right (GTK_WIDGET(grid), 25);

  gtk_container_add (GTK_CONTAINER(window), GTK_WIDGET(grid));
  g_print ("grid after gtk_container_add : floating = %d\n\n", is_floating(grid));
  g_print ("window after gtk_container_add : floating = %d\n\n", is_floating(window));

  gtk_widget_show_all (GTK_WIDGET(window));
  g_print ("window after gtk_widget_show_all : floating = %d\n\n", is_floating(window));
  gtk_main();
  return(0);
}


Program output:
Code:
window after gtk_window_new() : floating = 0

button after gtk_button_new_with_label : floating = 1

grid after gtk_grid_new : floating = 1

button after gtk_grid_attach : floating = 0

grid after gtk_grid_attach : floating = 1

grid after gtk_container_add : floating = 0

window after gtk_container_add : floating = 0

window after gtk_widget_show_all : floating = 0


Top
 Profile  
 
 Post subject: Re: Gtk memory management in a nutshell
PostPosted: Sun Jan 27, 2013 11:40 pm 
Offline

Joined: Sun Jan 27, 2013 9:23 pm
Posts: 1
Location: UK
The leak I think (need to create a suppression file for valgrind false positives) is indeed at the line when g_strdup allocates memory for "Sample %d", i + 1. List store takes the pointer copies the array of gchar's into newly allocated memory owned by the list store; However the original copy is not g_free'd as per reference manual. One solution that springs to mind is :
Code:
for( i = 0; i < 5; i++ )
    {
        ...
        gchar* text = g_strdup_printf( "Sample %d", i + 1 );
        ...
        gtk_list_store_set( store, &iter,
                            COL_TEXT, text,
                            COL_IMG, pixbuf,
                            -1 );

        ...
        g_free(text);
      }

_________________
marcoose777


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group