I created a simple image viewer in gtkD, which eats up memory every time a new image is displayed. The "same" program in C doesn't show that issue. Looking into src/gdk/Pixbuf.d I can't see the problem immediately.

What am I doing wrong?

C code:

// gcc dvgtk.c $(pkg-config --cflags --libs gtk+-3.0) -o dvgtk

#include <gtk/gtk.h>

static	GError		*err = NULL;
static	GdkPixbuf	*pixbuf = NULL;
static	GtkWidget	*window, *image;
static	int		filenameidx;
static	char const	*pfilenames[] = {
						"image1.jpg",
						"image2.jpg"
					};

static gboolean TimeoutFunc(gpointer user_data)
{
	char const *pfilename;

	pfilename = pfilenames[filenameidx];
	if (++filenameidx >= sizeof(pfilenames) / sizeof(pfilenames[0])) filenameidx = 0;

	if (pixbuf != NULL) {
		g_object_unref(pixbuf);
	}

	pixbuf = gdk_pixbuf_new_from_file(pfilename, &err);
	g_assert_no_error(err);

	gtk_image_set_from_pixbuf((GtkImage*)image, pixbuf);
}

int main( int argc, char *argv[])
{
	int idx;

	gtk_init(&argc, &argv);

	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
	gtk_window_set_default_size(GTK_WINDOW(window), 600, 400);
	gtk_window_set_title(GTK_WINDOW(window), "Image Test");
	gtk_window_set_resizable(GTK_WINDOW(window), FALSE);

	image = gtk_image_new();
	gtk_container_add(GTK_CONTAINER(window), image);

	g_timeout_add(1000, TimeoutFunc, NULL);

	g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
	gtk_widget_show_all(window);

	gtk_main();

	return 0;
}

D code:

import core.memory;

import glib.Timeout;

import gdk.Pixbuf;

import gtk.Image;
import gtk.Main;
import gtk.MainWindow;

private class MyMainWindow : MainWindow
{
	private	int		iteration;
	private	Pixbuf		pixbuf;
	private	Image		image;
	private	size_t		filenameIdx;
	private	string[]	filenames = [
							"image1.jpg",
							"image2.jpg",
						];

	public this(string text)
	{
		super(text);

		this.setDefaultSize(600, 400);
		this.setResizable(false);

		this.image = new Image();
		this.add(this.image);

		new Timeout(1000, &this.cbTimer);
	}

	private bool cbTimer()
	{
		auto filename = this.filenames[this.filenameIdx];
		if (++this.filenameIdx >= this.filenames.length) this.filenameIdx = 0;

		if (this.pixbuf) {
			// these lines don't help anything and can be left out
			this.pixbuf.destroy();
			this.pixbuf = null;
			core.memory.GC.collect();
		}
		this.pixbuf = new Pixbuf(filename);
		this.image.setFromPixbuf(this.pixbuf);

		// stop after 20 image switches to avoid out of memory
		return ++this.iteration < 20;
	}
}

void main(string[] args)
{
	Main.init(args);
	MainWindow win = new MyMainWindow("Image Test");
	win.showAll();

	Main.run();
}

Of course, the actual program is way bigger.

Replace "image1.jpg" and "image2.jpg" with names existing on your system.

Here's some valgrind output. Note that the blocks are meantioned 19 times (which is the switch count of the images):

==00:00:22:00.319 14646== 3,192 (1,520 direct, 1,672 indirect) bytes in 19 blocks are definitely lost in loss record 7,409 of 7,506
==00:00:22:00.319 14646==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==00:00:22:00.319 14646==    by 0x5C75610: g_malloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:22:00.319 14646==    by 0x5C8B22D: g_slice_alloc (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:22:00.319 14646==    by 0x5C8B76D: g_slice_alloc0 (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:22:00.320 14646==    by 0x9351DB1: g_type_create_instance (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4002.0)
==00:00:22:00.320 14646==    by 0x9336354: ??? (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4002.0)
==00:00:22:00.320 14646==    by 0x93384C3: g_object_new_valist (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4002.0)
==00:00:22:00.320 14646==    by 0x93388A3: g_object_new (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.4002.0)
==00:00:22:00.320 14646==    by 0x6A2FD2B: gdk_pixbuf_new_from_data (in /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0.3000.7)
==00:00:22:00.320 14646==    by 0x6A2DA01: gdk_pixbuf_new (in /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0.3000.7)
==00:00:22:00.320 14646==    by 0x11BE46E0: ??? (in /usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-jpeg.so)
==00:00:22:00.320 14646==    by 0x6A31B41: gdk_pixbuf_new_from_file (in /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0.3000.7)
==00:00:22:00.320 14646==    by 0x6D0492: _D3gdk6Pixbuf6Pixbuf6__ctorMFAyaZC3gdk6Pixbuf6Pixbuf (in dvgtkd)
==00:00:22:00.320 14646==    by 0x6B1755: _D6dvgtkd12MyMainWindow7cbTimerMFZb (dvgtkd.d:54)
==00:00:22:00.320 14646==    by 0x6F3648: _D4glib7Timeout7Timeout16callAllListenersMFZb (in dvgtkd)
==00:00:22:00.320 14646==    by 0x6F35E4: _D4glib7Timeout7Timeout15timeoutCallbackUC4glib7Timeout7TimeoutZb (in dvgtkd)
==00:00:22:00.320 14646==    by 0x5C70702: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:22:00.320 14646==    by 0x5C6FCE4: g_main_context_dispatch (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:22:00.320 14646==    by 0x5C70047: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:22:00.320 14646==    by 0x5C70309: g_main_loop_run (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:22:00.320 14646==    by 0xC2E8FE4: gtk_main (in /usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1000.8)
==00:00:22:00.320 14646==    by 0x813162: _D3gtk4Main4Main3runFZv (in dvgtkd)
==00:00:22:00.320 14646==    by 0x6B17EF: _Dmain (dvgtkd.d:67)
==00:00:22:00.320 14646==    by 0x950252: _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZ9__lambda1MFZv (in dvgtkd)
==00:00:22:00.320 14646==    by 0x9501A5: _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ7tryExecMFMDFZvZv (in dvgtkd)
==00:00:22:00.320 14646==    by 0x95020B: _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZv (in dvgtkd)
==00:00:22:00.320 14646==    by 0x9501A5: _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ7tryExecMFMDFZvZv (in dvgtkd)
==00:00:22:00.320 14646==    by 0x950126: _d_run_main (in dvgtkd)
==00:00:22:00.320 14646==    by 0x6BFE9C: main (in dvgtkd)
==00:00:22:00.320 14646== 
...
==00:00:22:00.320 14646== 
==00:00:22:00.320 14646== 745,524,612 bytes in 19 blocks are possibly lost in loss record 7,506 of 7,506
==00:00:22:00.320 14646==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==00:00:22:00.320 14646==    by 0x6A2D9CC: gdk_pixbuf_new (in /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0.3000.7)
==00:00:22:00.320 14646==    by 0x11BE46E0: ??? (in /usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-jpeg.so)
==00:00:22:00.320 14646==    by 0x6A31B41: gdk_pixbuf_new_from_file (in /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0.3000.7)
==00:00:22:00.320 14646==    by 0x6D0492: _D3gdk6Pixbuf6Pixbuf6__ctorMFAyaZC3gdk6Pixbuf6Pixbuf (in dvgtkd)
==00:00:22:00.360 14646==    by 0x6B1755: _D6dvgtkd12MyMainWindow7cbTimerMFZb (dvgtkd.d:54)
==00:00:22:00.360 14646==    by 0x6F3648: _D4glib7Timeout7Timeout16callAllListenersMFZb (in dvgtkd)
==00:00:22:00.360 14646==    by 0x6F35E4: _D4glib7Timeout7Timeout15timeoutCallbackUC4glib7Timeout7TimeoutZb (in dvgtkd)
==00:00:22:00.360 14646==    by 0x5C70702: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:22:00.360 14646==    by 0x5C6FCE4: g_main_context_dispatch (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:22:00.360 14646==    by 0x5C70047: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:22:00.360 14646==    by 0x5C70309: g_main_loop_run (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:22:00.360 14646==    by 0xC2E8FE4: gtk_main (in /usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1000.8)
==00:00:22:00.360 14646==    by 0x813162: _D3gtk4Main4Main3runFZv (in dvgtkd)
==00:00:22:00.360 14646==    by 0x6B17EF: _Dmain (dvgtkd.d:67)
==00:00:22:00.360 14646==    by 0x950252: _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZ9__lambda1MFZv (in dvgtkd)
==00:00:22:00.360 14646==    by 0x9501A5: _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ7tryExecMFMDFZvZv (in dvgtkd)
==00:00:22:00.360 14646==    by 0x95020B: _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZv (in dvgtkd)
==00:00:22:00.360 14646==    by 0x9501A5: _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ7tryExecMFMDFZvZv (in dvgtkd)
==00:00:22:00.360 14646==    by 0x950126: _d_run_main (in dvgtkd)
==00:00:22:00.360 14646==    by 0x6BFE9C: main (in dvgtkd)

I'm using GtkD-2.4.0, but had the same problem with GtkD-2.3.1.

Thanks for your help,
Diez

PS: Got

500 - Internal Server Error

Internal Server Error

Internal error information:
object.Exception@../../../root/.dub/packages/vibe-d-0.7.19/source/vibe/utils/dictionarylist.d(134): Accessing non-existent key 'confirmemail'.

vibenews(pure @safe bool std.exception.enforce!(bool).enforce(bool, lazy const(char)[], immutable(char)[], uint)+0x22) [0x846ded2]
vibenews(const(pure @safe immutable(char)[] function(immutable(char)[])) vibe.utils.dictionarylist.DictionaryList!(immutable(char)[], true).DictionaryList.opIndex+0x6c) [0x83e0344]
vibenews(void vibenews.web.WebInterface.postArticle(vibe.http.server.HTTPServerRequest, vibe.http.server.HTTPServerResponse)+0x1fc) [0x830692c]
vibenews(void vibe.http.router.URLRouter.handleRequest(vibe.http.server.HTTPServerRequest, vibe.http.server.HTTPServerResponse)+0xff) [0x83cd837]
vibenews(bool vibe.http.server.handleRequest(vibe.core.stream.Stream, vibe.core.net.TCPConnection, vibe.http.server.HTTPServerListener, ref vibe.http.server.HTTPServerSettings, ref bool)+0xf74) [0x83d1f2c]
vibenews(void vibe.http.server.handleHTTPConnection(vibe.core.net.TCPConnection, vibe.http.server.HTTPServerListener)+0xc7) [0x83d0f07]
vibenews(void vibe.http.server.listenHTTPPlain(vibe.http.server.HTTPServerSettings, void delegate(vibe.http.server.HTTPServerRequest, vibe.http.server.HTTPServerResponse)).doListen(vibe.http.server.HTTPServerSettings, vibe.http.server.HTTPServerListener, immutable(char)[]).lambda4(vibe.core.net.TCPConnection)+0x1f) [0x83ce2f7]<br>vibenews(void vibe.core.drivers.libevent2_tcp.onConnect(int, short, void*).ClientTask.execute()+0x221) [0x837e081]<br>vibenews(_D4vibe4core4core12T7runTaskZ7runTaskFDFZvZS4vibe4core4task4Task12callDelegateFC4vibe4core4core8CoreTaskZv+0x1f) [0x836ff17]
vibenews(void vibe.core.core.CoreTask.run()+0xca) [0x836d252]
vibenews(void core.thread.Fiber.run()+0x21) [0x8482171]
vibenews(fiber_entryPoint+0x49) [0x84820a9]
[(nil)]