GNOME Bugzilla – Bug 786975
GtkHeaderBar causes a memory leak
Last modified: 2017-08-31 18:57:43 UTC
Hello, I have a simple c++ object wrapper over gtk+ window. After after installing the object once and put it to bi closed/opened in a interval loop I got huge memory leak, and I can not figure out where. 3 methods of object are used and described on pastebin link. https://pastebin.com/VBQTz73i interva loop looks like: // on paste bin I forgot to add definition of get_name() method std::string nwTabWindow::get_name() const { std::lock_guard<std::mutex> g(win_data->prop_mutex); return win_data->name; } gboolean switcher(gpointer data){ nwTabWindow*win = (nwTabWindow*)data; if(win->is_opened()) win->unsafe_close(); else { win->unsafe_open(); win->unsafe_show(); } return true; } nwTabWindow*w = new nwTabWindow("title","name"); g_timeout_add(100,switcher,w); I got over 30 mb leeks in less then 20 minutes and still continue to spend memory. Can you pleas explain where I do wrong? Also good to know I use css styling where photos are involved, hope this is not a issue case. Only top level (in this case popup) window is destroyed while closing window so by documentation it should deallocate its children or I must to remove every child of windows before destroying it? I know webkitgtk has leeks but is not used in this test. I tried to make many other changes but with no success. Peace of code is isolated and only that part is running during the test, so I am sure leak is in the 3 functions. unsafe_show, unsafe_open, unsafe_close. Pleas help.
That's an awful lot of code, in a poorly punctuated mix of C and C++, to expect other people to read and painstakingly analyse so you don't have to. This isn't a place for code review/debugging, only specific problems that you know exist. It's expected that you perform a minimum of debugging yourself before posting here. First, run your program with valgrind, which should quickly reveal sources of leaks. If you find any that are conclusively the default of GTK+, not your program, then reopen this and post all the relevant valgrind output. Secondly, consider using the official C++ bindings, gtkmm, instead of rolling your own.
Ok, will make minimum debugging example as soon as I get time for it. I am sure it is in gtk. It is a little bit late to use gtkmm since project is big and integrated with c gtk, and also not sure is it possible to compile gtkmm to be used on windows OS, beside that I can not use valgrind on windows.
(In reply to zninja from comment #2) > Ok, will make minimum debugging example as soon as I get time for it. Thank you. > also not sure is it possible to compile gtkmm to be used on > windows OS It's extremely possible! I use MSYS2, which includes ready-to-use packages of gtkmm and related libraries (GTK+, GLib, other mm libs) in its distribution. MSYS2 is also the currently recommended way to compile GTK+ itself on Windows: https://www.gtk.org/download/windows.php > beside that I can not use valgrind on windows. True, I didn't pay attention to the "Hardware" field. If you can use some other memory diagnostic to narrow down the possible causes, please do.
(In reply to Daniel Boles from comment #3) > If you can use some other memory diagnostic to narrow down the possible > causes, please do. For instance, GCC and Clang can offer the ASan address sanitiser as a plugin for super easy analysis of programs compiled in special debug modes - though you may need to obtain separate packages of those compilers for that, as I don't believe the ones in MSYS2 offer the plugins. If you prefer MSVC, maybe there is a way there too.
It is an GtkHeaderBar cause leak or I do not using it properly. Debugging code: GtkWidget *win = nullptr; gboolean cb(gpointer user_data) { if(win != nullptr){ gtk_widget_destroy(win); win= nullptr; return true; } win = gtk_window_new(GTK_WINDOW_POPUP); GtkWidget *title = gtk_header_bar_new(); gtk_container_add(GTK_CONTAINER(win),title); gtk_widget_show_all(win); return true; } int main (int argc,char *argv[]) { gtk_init (&argc, &argv); g_timeout_add(100,cb,nullptr); gtk_main (); return 0; } Compiled with mingw64-g++ in linux centos 7 environment, executed on virtual box windows 7. I did not found a tool to confirm that on windows, but if you open task manager you will see program memory usage will rise and never stop rising.
I presume you say that because the leak did not occur until you added the HeaderBar? I don't see anything really wrong with that (other than cosmetics: you should add the headerbar with gtk_window_set_titlebar() normally) but will try to test another time with valgrind.
I tested to add titlebar, subtitle, costum title.. etc in hope memory usage will change, but nothing. I make code without cosmetics to make debugging code minimal as possible. Yes, when I remove HeaderBar from my project everything is ok. Also in this code example. If I put button instead of hederbar, memory usage is consumed at normal level.
Thanks for that info. You tagged the version as 3.12. Is that correct? Does this still occur on the latest version of 3.22 you can get? An 0.10 version jump ought to cover a lot of plugged leaks. If the problem only exists in 3.12, that's obsolete at this point, so it's not really supportable. Anyway as of 3.22, on a cursory glance, one thing that immediately jumps out to me in gtkheaderbar.c is that the GList priv->children is not g_list_free()d. That seems like a start. I'll test later and see what valgrind thinks with/without it.
(Also, that GList looks like it could be a GSList, to reduce memory consumption even after the leak is plugged)
If that GList causes a memory leak, there's a problem somewhere else since GtkContainer will remove all child widgets in destroy which will free all the list elements and an empty linked list is just NULL.
(In reply to Daniel Boles from comment #8) > Thanks for that info. > > You tagged the version as 3.12. Is that correct? Does this still occur on > the latest version of 3.22 you can get? An 0.10 version jump ought to cover > a lot of plugged leaks. If the problem only exists in 3.12, that's obsolete > at this point, so it's not really supportable. > > > Anyway as of 3.22, on a cursory glance, one thing that immediately jumps out > to me in gtkheaderbar.c is that the GList priv->children is not > g_list_free()d. That seems like a start. I'll test later and see what > valgrind thinks with/without it. I am glad to help. :) Yes, 3.12 is in my case. https://image.prntscr.com/image/Qc1IiFlkSgmra0YmZreNfg.png
Also for mingw 3.12 is latest version I can get without compiling by my self. For linux I can get 3.14 as latest without compiling by my self and have an same effects as in 3.12, tested just now.
(In reply to zninja from comment #12) > Also for mingw 3.12 is latest version I can get without compiling by my self. As I mentioned in Comment 3, you might want to install MSYS2, as it offers prebuilt packages GTK+ 3.22 and gets point releases pretty soon after they come out. It's a combination of a small set of Cygwin-like tools plus fully-fledged 32- and 64-bit versions of MinGW-w64 (a more developed fork/replacement of MinGW. It's the recommended way to get GTK+ on Windows, and it's also a great way to get many other Linux/POSIX-like tools/packages and build software with them. I'll post back when I'm able to profile this in 3.22 with valgrind, but if you get a chance to test that version with your code before then, that would be good.
Thanks, right now project is close to the end, as deadlines too. So it will be fine for this version to avoid using headerbar. Maybe will try with MSYS2 to build next version when it comes to that.
Created attachment 358793 [details] Log file from valgrind Test code: #include <gtk/gtk.h> GtkWidget *win = nullptr; int x = 0; gboolean cb(gpointer user_data) { if(win != nullptr){ gtk_widget_destroy(win); win= nullptr; if(x > 100){ gtk_main_quit(); return false; } return true; } win = gtk_window_new(GTK_WINDOW_POPUP); GtkWidget *title = gtk_header_bar_new(); gtk_container_add(GTK_CONTAINER(win),title); gtk_widget_show_all(win); x++; return true; } int main (int argc,char *argv[]) { gtk_init (&argc, &argv); g_timeout_add(100,cb,nullptr); gtk_main (); return 0; }
Thanks, but I don't know if that gives us much to work with, for various reasons: How long did you run the program for to get this? You mentioned leaks on the scale of 30 MB, but that looks like about 20 kB. What memory usage occurred? You are missing debug symbols from GLib, GTK+, and other libraries, which makes it mostly a guessing game as to what the leaks are, or whether they are really. Example: there is nothing related to GtkHeaderBar there, so it remains a mystery. On that note, ideally you could install any valgrind suppression files available for the listed libraries - to clear confusion of all the "possibly lost" entries. Finally if you could turn off any extensions like the Oxygen theming engine, that would be useful, though it doesn't look like a particular culprit here.
Also, it looks like you ran with a limited depth of traces; please try increasing valgrind's --num-callers so that we have more chance of ascertaining the true origin of allocations, once we have the symbols.
Yes, you are right, I run it in release mode. Sorry for that. Pleas give me full valgrind command line you want to execute for test. I can do the test in the morning.
Thanks, there are 2 main things to do: Definitely install the debug symbols for GTK+/GLib/Pango, and if you can get any for the other libraries in the log, that'd be great. Then the only change I would make would be to add --num-callers=30 to your command line; that _should_ be enough to get a full backtrace for each leak (we can add more if needed for any particular leak).
Like Daniel said, you need: - debugging symbols for every library, in order to get a decent stack trace - increase the number of callers reported Additionally, since you're using hopelessly outdated versions of the libraries in the stack, you should also ensure you're disabling the GLib slice allocator, otherwise Valgrind will have a hard time figuring out what gets freed. Set these two environment variables before launching Valgrind G_SLICE=always-malloc G_DEBUG=gc-friendly You also want to disable the Oxygen theme engine, to avoid spurious allocations coming from it (and if you update to a recent version of GTK+, the Oxygen theme engine library won't be used anyway). Finally, you want to use the suppression file for GLib: https://git.gnome.org/browse/glib/tree/glib.supp to reduce the amount of false positives. Looking at the Valgrind log in attachment 358793 [details] I only see one-off allocations and random noise. How were you measuring the leak in the first place, if you weren't using Valgrind? Creating and destroying top-level widgets in a loop won't "leak" anything per se; GTK+ uses a slab allocator to group small allocations together efficiently; you'll see a large-ish memory allocation once you pass certain thresholds, but the memory will be recycled, and eventually freed if unused.
Created attachment 358854 [details] New Vilgrind log file (In reply to Daniel Boles from comment #19) > Definitely install the debug symbols for GTK+/GLib/Pango, and if you can get > any for the other libraries in the log, that'd be great. (In reply to Emmanuele Bassi (:ebassi) from comment #20) > - debugging symbols for every library, in order to get a decent stack trace Sorry could not find debugging libs. added G_SLICE=always-malloc G_DEBUG=gc-friendly Before test. Downloaded https://git.gnome.org/browse/glib/tree/glib.supp file. Command line used: valgrind --leak-check=yes --num-callers=30 --suppressions=/root/Downloads/glib.supp ./gtk_leak_test > log.txt 2>&1 (In reply to Emmanuele Bassi (:ebassi) from comment #20) > How were you measuring the leak in the first place, if you weren't using > Valgrind? > > Creating and destroying top-level widgets in a loop won't "leak" anything > per se; GTK+ uses a slab allocator to group small allocations together > efficiently; you'll see a large-ish memory allocation once you pass certain > thresholds, but the memory will be recycled, and eventually freed if unused. It is not so hard to conclude when program has memory leak while running. I opened task manager, started program, and start using it. After some while memory usage should hit the limits right? And should not rise for same calls if you free all allocated memory if first place, otherwise will rise memory until you are out of resources. I write my own html parser to feet my needs since libxml2 xpath is not giving me what I needed. I fixed small leaks there, after which program worked good. After modifying my windows wrapper, program started to consume memory after each cycle where window (top level GtkWindow) is destroyed and opened again as in code of my first post or test example, and continue to grow memory usage more and more until end of program execution without freeing all memory. So if program is not recycling all used memory it is obvious there is a memory leak, no need for a tool to conclude is it a leak or not, you just need to observer memory usage how it is used in my case in task manager on windows os right? Tool is good for faster finding source of leak if you are not willing to search manually which is almost impossible in big project like gtk. Later I isolated each widget one by one and put in test example to observer memory. Like I said in comment No#7 if I put other widget like button for example in test example, program use memory as you described > Creating and destroying top-level widgets in a loop won't "leak" anything > per se; GTK+ uses a slab allocator to group small allocations together > efficiently; you'll see a large-ish memory allocation once you pass certain > thresholds, but the memory will be recycled, and eventually freed if unused. But with headerbar program after each new headerbar creation it rise its memory for about 100kb over and over again and eventually will probably take over all memory resources. So what I would do: Would put widget which has same abstracted base classes like header bar to see if it is a same case. If it is , will check abstracted base class one level up from it. If it is not, then will compare what this two widget don't have in common and start from there. I presume that headerbar uses calls that are used in other widgets too, so if other widgets run right, I am sure that is something in headerbar which is not handled right, presume it is an destroy process of headerbar is not freeing all allocated memory where most of leaks happen in programs. (In reply to Daniel Boles from comment #16) > How long did you run the program for to get this? You mentioned leaks on the > scale of 30 MB, but that looks like about 20 kB. What memory usage occurred? If you take a look test example you will see that loop is running in time out of 100 milliseconds and it takes two loops to complete the cycle. So it is run about 20 seconds. You can calculate the end if you run it for 20 minutes.
(In reply to zninja from comment #21) > Sorry could not find debugging libs. Then there's no point posting anything. This is still completely uninformative. You also didn't disable the Oxygen theme engine as requested.
(In reply to Daniel Boles from comment #22) > Then there's no point posting anything. This is still completely > uninformative. > > You also didn't disable the Oxygen theme engine as requested. Sorry for that. There is an enough info where to start searching. If you are able to test "test example" in latest gtk version and you do not get same results like me, then problem do not exists which is great, otherwise you can try with debug libs to get vilgrind output which I am not able right now to supply. When I finish the project, I will go into gtk source and try to find where is a leak and propose a patch if someone does not patch it until then.
(In reply to zninja from comment #23) > There is an enough info where to start searching. Relative to now, there was the same amount of info to start searching when you figured out the widget that triggers this, way back in Comment 5. :P > If you are able to test "test example" in latest gtk version and you do not > get same results like me, then problem do not exists which is great, I have just tried this, albeit on Windows without valgrind, using GTK+ 3.22 - and can replicate the steadily increasing RAM usage, as viewed in the Task Manager. but here's where it gets funny: commenting-out the HeaderBar does not resolve it; if anything, it makes the RAM usage increase faster... What DOES fix the problem is making the window a GTK_WINDOW_TOPLEVEL. Then we can even add the HeaderBar back in, and no leaks appear to occur. So the problem seems to be the GTK_WINDOW_POPUP type. I've not looked into this much yet. It may be completely expected; it may not.
(In reply to Daniel Boles from comment #24) > So the problem seems to be the GTK_WINDOW_POPUP type. Which may, of course, implicate the GDK_WINDOW_TEMP type in gdk/win32 specifically. Before I get back onto a Linux box to look at this - you mentioned having a Linux machine. Are you able to test this running natively on that?
(In reply to Daniel Boles from comment #24) > but here's where it gets funny: commenting-out the HeaderBar does not > resolve it; if anything, it makes the RAM usage increase faster... I then noticed that without a child to fit, the default size of the window is larger. Then I had a thought... and guess what? Making the default-size of the GTK_WINDOW_POPUP larger increases the leak proportionally. So I guess this is caused by not freeing a graphics surface somewhere or similar.
For me is not the case. I tested with GTK_WINDOW_TOPLEVEL on both, windows and linux. And occurs are the same, on linux I compiled native gtk for linux with linux distribution of g++ On linux I used htop to observer memory. Yes, I have linux machine centos 7 64 bit with kde environment, I am using windows on virtual box just for tests since it is more convenient for me then to use wine. If your case is different, then you are right that problem is in GTK_WINDOW_POPUP. But probably if there was a changes in GTK_WINDOWS_TOPLEVEL from my version 3.12 to yours, than some of changes are not affecting GTK_WINDOW_POPUP which probably should.
So let's assume there are two distinct issues, and address the first one first. The thing to have done right at the beginning of this - which I'm equally guilty of missing - is simply to check if any relevant commits have been made to gtkheaderbar.c since GTK+ 3.12. Well, the final commit on the gtk-3-12 branch was on Fri May 29 21:03:53 2015. I did a git log for gtkheaderbar.c on the gtk-3-22 branch, searched for "leak", and look at what we have, between the time of gtk-3-12 and now: https://bugzilla.gnome.org/show_bug.cgi?id=759132 https://bugzilla.gnome.org/show_bug.cgi?id=772859 That's 5 commits plugging a lot of leaks. Since I seem to have a different problem, but not caused by HeaderBar, the bug you opened this for appears to have been fixed, and you have only a few options: * update to a modern version of GTK+, which is always the preferable option * build your own 3.12 with these patches applied * not use HeaderBar if you for some reason can't use its latest, best version. This bug should probably then be closed as a duplicate of one of those, and I should open a new one for the GTK_WINDOW_POPUP thing, but first I need to ascertain whether or not it's Windows-specific.
*** This bug has been marked as a duplicate of bug 759132 ***
The leak with GTK_WINDOW_POPUP is filed as https://bugzilla.gnome.org/show_bug.cgi?id=787089
Running this under GTK+ 3.22 on Linux with valgrind reveals some leaks indeed, though nothing on the scale of what you reported seeing in the Task Manager, which is understandable given the bugs I linked earlier fixing leaks since 3.12. The "definite" leaks are in Pango or fontconfig, it seems, so I need to get debug symbols for that, and it'll need its own bug separate from this one.
(In reply to Daniel Boles from comment #31) > Running this under GTK+ 3.22 on Linux with valgrind reveals some leaks > indeed, though nothing on the scale of what you reported seeing in the Task > Manager, which is understandable given the bugs I linked earlier fixing > leaks since 3.12. Nothing in the Valgrind logs attached here show large leaks at all. > The "definite" leaks are in Pango or fontconfig, it seems, so I need to get > debug symbols for that, and it'll need its own bug separate from this one. Those are one-off allocations, not leaks.