Thanks for your reply, Matheus.

To avoid changing gtkD's Pixbuf.d, I sub-classed it. To verify its effect, I added printouts in the ctor and dtor:

import glib.Timeout;

import gdk.Pixbuf;

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

public class MyPixbuf : gdk.Pixbuf.Pixbuf
{
	public this(string filename)
	{
		super(filename);
		std.stdio.stdout.writeln("MyPixbuf");
	}

	~this()
	{
		std.stdio.stdout.writeln("~MyPixbuf");
		this.gdkPixbuf = null;
		this.unref();
	}
}

private class MyMainWindow : MainWindow
{
	private	int		iteration;
	private	MyPixbuf	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) {
			delete this.pixbuf;
		}
		this.pixbuf = new MyPixbuf(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);
	auto win = new MyMainWindow("Image Test");
	win.showAll();

	Main.run();
}

I get the following output:

MyPixbuf
~MyPixbuf
MyPixbuf
~MyPixbuf
(dvgtkd:21102): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed
MyPixbuf
~MyPixbuf
MyPixbuf
~MyPixbuf
(dvgtkd:21102): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed
MyPixbuf
~MyPixbuf
MyPixbuf
~MyPixbuf
(dvgtkd:21102): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed
MyPixbuf
~MyPixbuf
MyPixbuf
~MyPixbuf
(dvgtkd:21102): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed
MyPixbuf
~MyPixbuf
MyPixbuf
~MyPixbuf
(dvgtkd:21102): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed
MyPixbuf
~MyPixbuf
MyPixbuf
~MyPixbuf
(dvgtkd:21102): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed
MyPixbuf
~MyPixbuf
MyPixbuf
~MyPixbuf
(dvgtkd:21102): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed
MyPixbuf
~MyPixbuf
MyPixbuf
~MyPixbuf
(dvgtkd:21102): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed
MyPixbuf
~MyPixbuf
MyPixbuf
~MyPixbuf
(dvgtkd:21102): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed
MyPixbuf
~MyPixbuf
MyPixbuf
~MyPixbuf
(dvgtkd:21102): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed
(dvgtkd:21102): GLib-CRITICAL **: Source ID 7 was not found when attempting to remove it

Since 10 times the dtor had problems, the memory leak still remains for 9 blocks (why not 10?):

==00:00:01:23.908 21109== 272,753,892 bytes in 9 blocks are possibly lost in loss record 7,504 of 7,504
==00:00:01:23.908 21109==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==00:00:01:23.908 21109==    by 0x6A2D9CC: gdk_pixbuf_new (in /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0.3000.7)
==00:00:01:23.908 21109==    by 0x11BE46E0: ??? (in /usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-jpeg.so)
==00:00:01:23.908 21109==    by 0x6A31B41: gdk_pixbuf_new_from_file (in /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0.3000.7)
==00:00:01:23.908 21109==    by 0x6D0B82: _D3gdk6Pixbuf6Pixbuf6__ctorMFAyaZC3gdk6Pixbuf6Pixbuf (in dvgtkd)
==00:00:01:23.908 21109==    by 0x6B1784: _D6dvgtkd8MyPixbuf6__ctorMFAyaZC6dvgtkd8MyPixbuf (dvgtkd.d:24)
==00:00:01:23.908 21109==    by 0x6B1968: _D6dvgtkd12MyMainWindow7cbTimerMFZb (dvgtkd.d:73)
==00:00:01:23.908 21109==    by 0x6F3D38: _D4glib7Timeout7Timeout16callAllListenersMFZb (in dvgtkd)
==00:00:01:23.908 21109==    by 0x6F3CD4: _D4glib7Timeout7Timeout15timeoutCallbackUC4glib7Timeout7TimeoutZb (in dvgtkd)
==00:00:01:23.908 21109==    by 0x5C70702: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:01:23.908 21109==    by 0x5C6FCE4: g_main_context_dispatch (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:01:23.908 21109==    by 0x5C70047: ??? (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:01:23.908 21109==    by 0x5C70309: g_main_loop_run (in /lib/x86_64-linux-gnu/libglib-2.0.so.0.4002.0)
==00:00:01:23.908 21109==    by 0xC2E8FE4: gtk_main (in /usr/lib/x86_64-linux-gnu/libgtk-3.so.0.1000.8)
==00:00:01:23.908 21109==    by 0x813852: _D3gtk4Main4Main3runFZv (in dvgtkd)
==00:00:01:23.908 21109==    by 0x6B19FF: _Dmain (dvgtkd.d:87)
==00:00:01:23.908 21109==    by 0x950942: _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZ9__lambda1MFZv (in dvgtkd)
==00:00:01:23.909 21109==    by 0x950895: _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ7tryExecMFMDFZvZv (in dvgtkd)
==00:00:01:23.909 21109==    by 0x9508FB: _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZv (in dvgtkd)
==00:00:01:23.909 21109==    by 0x950895: _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ7tryExecMFMDFZvZv (in dvgtkd)
==00:00:01:23.909 21109==    by 0x950816: _d_run_main (in dvgtkd)
==00:00:01:23.909 21109==    by 0x6C058C: main (in dvgtkd)

Why is the dtor failing every 2nd time? Can someone explain that?

What causes this message:
Source ID 7 was not found when attempting to remove it

Diez