GNOME Bugzilla – Bug 633869
new Gdk.Pixbuf.from_file leaks if reference stored in Gee collection or array
Last modified: 2010-11-08 19:51:36 UTC
I'm seeing this with both 0.10.1 and 0.11.1. Spotted this on Stack Overflow: http://stackoverflow.com/questions/3703112/gdk-pixbuf-memory-leak-in-vala-or-something-else I'm attaching a reduced test case that reproduces the problem both when pixbufs loaded from disk are stored in either a Gee collection or an array. valgrind reports the memory leaks coming from gdk_pixbuf_new. When I run this program the memory grows to ~90MB in system monitor although the pixbufs should be freed before the program prompts for user input.
Created attachment 173736 [details] Test case for both arrays and Gee collections
Created attachment 173933 [details] modified test case As far as I can tell, Vala and libgee are both doing their job perfectly. The pixbuf data does get unrefed, and according to valgrind it does get freed. I've attached a modified version of the test case which will use a GWeakNotify to report to the program when each pixbuf gets destroyed. As you can see, they all do, so if there is a leak it doesn't seem to be Vala or libgee's fault. Running this in valgrind's leak-check tool reveals 0 definitely lost blocks, although there are a few possibly lost (the largest offender was 660 blocks--nowhere near the 20000). When you use the massif tool in valgrind to profile heap usage, you'll get something like this (apologies if it wraps, but you can easily generate something similar yourself. See http://valgrind.org/docs/manual/ms-manual.html): MB 90.92^ : | :# :: | @:# :::: | @@:@:# ::@::: | @@ :@:# ::::@::: | @@@@ :@:# :::::@::: | @@@@@@ :@:# @::::::@::: | :@@@@@@@ :@:# ::@::::::@::: | :::@@@@@@@ :@:# ::::@::::::@::: | ::::@@@@@@@ :@:# :::::@::::::@::: | @@::::@@@@@@@ :@:# :@:::::@::::::@::: | @:@@::::@@@@@@@ :@:# :::@:::::@::::::@::: | @@@ @@::::@@@@@@@ :@:# ::: :@:::::@::::::@::: | @@@ @ @@::::@@@@@@@ :@:# :: : :@:::::@::::::@::: | :@@@ @ @@::::@@@@@@@ :@:# :::: : :@:::::@::::::@::: | @@@:@@@ @ @@::::@@@@@@@ :@:# ::: :: : :@:::::@::::::@::: | @@ :@@@ @ @@::::@@@@@@@ :@:# :::::: :: : :@:::::@::::::@::: | @@@@@ :@@@ @ @@::::@@@@@@@ :@:# ::: :::: :: : :@:::::@::::::@::: | @@@@ @@ :@@@ @ @@::::@@@@@@@ :@:# :::: :::: :: : :@:::::@::::::@::: | :@@@@ @@ :@@@ @ @@::::@@@@@@@ :@:# :::::: :::: :: : :@:::::@::::::@::: 0 +----------------------------------------------------------------------->Gi 0 13.44 Exactly what you would expect. Memory usage spikes twice, to roughly the same level, once for each time you have 10000 pixbufs sitting around. If you call the test functions in a loop, you'll see that memory usage doesn't really grow beyond that 90 mb level, no matter how many times you call them. As for the discrepancy between valgrind and gnome-system-monitor, this is probably because most mallocs can't always return memory to the operating system when it is freed. I'm not exactly an expert in the area, having never really hacked on a malloc implementation, by my understanding of the problem is that malloc implementations basically have two choices for getting memory from the OS to parcel out: brk or mmap. brk is a lot faster, so that is what most implementations use (including ptmalloc2, which is what glibc uses). If you take a look at the man page for brk you'll quickly see the problem: you get one block of memory, and all you can do is decide how big it is. That means that if you have one byte of data one gigabyte into that block, your program will be unable to return that entire gigabyte to the OS. There are a couple ways to deal with this. The first is to just let the OS handle it... the unused memory will be swapped as soon as it is needed by another application, so it probably isn't going to be a big deal. The other option is to handle operations that will spike the memory usage in another process and communicate the result (D-Bus or some other IPC mechanism will work fine, but for simple stuff g_spawn_async_with_pipes will work well, and it's a lot cheaper). I'm going to go ahead and close this as invalid, but please feel free to reopen it if you feel that is incorrect.
I think your reasoning and investigation are both sound. I'm glad this is a non-issue -- I was going crazy looking at the C code trying to find the problem.