diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java index e2d8c831cc1..7449b612b5f 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java @@ -16,6 +16,7 @@ import java.util.*; import java.util.List; +import java.util.function.*; import java.util.stream.*; import org.eclipse.swt.*; @@ -494,9 +495,11 @@ public void copyArea (Image image, int x, int y) { private abstract class ImageOperation extends Operation { private Image image; + private final Consumer disposeCallback = this::setCopyOfImage; + ImageOperation(Image image) { setImage(image); - image.addOnDisposeListener(this::setCopyOfImage); + image.addOnDisposeListener(disposeCallback); } private void setImage(Image image) { @@ -515,6 +518,11 @@ protected Image getImage() { return image; } + @Override + void disposeAll() { + image.removeOnDisposeListener(disposeCallback); + super.disposeAll(); + } } private class CopyAreaToImageOperation extends ImageOperation { diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java index 84147c25e0c..2e665685127 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java @@ -1020,10 +1020,18 @@ void addOnDisposeListener(Consumer onDisposeListener) { onDisposeListeners.add(onDisposeListener); } +void removeOnDisposeListener(Consumer onDisposeListener) { + if (onDisposeListeners == null) { + return; + } + onDisposeListeners.remove(onDisposeListener); +} + @Override public void dispose() { if (onDisposeListeners != null) { onDisposeListeners.forEach(listener -> listener.accept(this)); + onDisposeListeners.clear(); } super.dispose(); } diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java index 559438997a8..6604b61b9f5 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java @@ -29,6 +29,7 @@ import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.junit.jupiter.api.Assumptions.assumeTrue; +import java.lang.ref.WeakReference; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.swt.SWT; @@ -804,6 +805,24 @@ public void test_bug1288_createGCFromImageFromNonDisplayThread() throws Interrup assertNull(exceptionReference.get(), "Creating a GC from an Image without a device threw an exception"); } +// Tests that a GC instance is cleaned from memory after it was disposed. +// Primarily supposed to test that the operations (currently only implemented for Windows) do not produce any leaks. +@Test +public void test_noMemoryLeakAfterDispose() { + GC testGC = new GC(display); + Image image = new Image(display, 1, 1); + testGC.setFont(display.getSystemFont()); + testGC.setClipping(new Rectangle(0, 0, 2, 2)); + testGC.drawImage(image, 0, 0); + testGC.drawText("Test", 0, 0); + testGC.drawLine(0, 0, 5, 5); + WeakReference testGCReference = new WeakReference<>(testGC); + testGC.dispose(); + testGC = null; + System.gc(); + assertNull(testGCReference.get()); +} + /* custom */ Display display; Shell shell;