Bug 573573 - [GTK3] Tree.rendererRender() leaks native memory
Summary: [GTK3] Tree.rendererRender() leaks native memory
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 4.9   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: 4.21 M1   Edit
Assignee: Simeon Andreev CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-05-17 03:44 EDT by Simeon Andreev CLA
Modified: 2021-06-10 03:48 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Simeon Andreev CLA 2021-05-17 03:44:00 EDT
To reproduce, run the following snippet (with VM arguments "") and observe growing memory consumption:

public class TestTreeLeak {

  public static void main(String[] args) {
    Display display = new Display();
    final Image image = display.getSystemImage(SWT.ICON_INFORMATION);
    Shell shell = new Shell(display);
    shell.setText("Test Tree leak based on SWT example: Images on the right side of the TreeItem");
    shell.setLayout(new FillLayout());
    Tree tree = new Tree(shell, SWT.MULTI | SWT.FULL_SELECTION);
    tree.setHeaderVisible(true);
    tree.setLinesVisible(true);
    int columnCount = 4;
    for (int i = 0; i < columnCount; i++) {
      TreeColumn column = new TreeColumn(tree, SWT.NONE);
      column.setText("Column " + i);
    }
    List<TreeItem> items = new ArrayList<>();
    int itemCount = 3;
    for (int i = 0; i < itemCount; i++) {
      TreeItem item1 = new TreeItem(tree, SWT.NONE);
      item1.setText("item " + i);
      for (int c = 1; c < columnCount; c++) {
        item1.setText(c, "item [" + i + "-" + c + "]");
      }
      items.add(item1);
      for (int j = 0; j < itemCount; j++) {
        TreeItem item2 = new TreeItem(item1, SWT.NONE);
        item2.setText("item [" + i + " " + j + "]");
        for (int c = 1; c < columnCount; c++) {
          item2.setText(c, "item [" + i + " " + j + "-" + c + "]");
        }
        items.add(item2);
        for (int k = 0; k < itemCount; k++) {
          TreeItem item3 = new TreeItem(item2, SWT.NONE);
          item3.setText("item [" + i + " " + j + " " + k + "]");
          for (int c = 1; c < columnCount; c++) {
            item3.setText(c, "item [" + i + " " + j + " " + k + "-" + c + "]");
          }
          items.add(item3);
        }
      }
    }
    /*
     * NOTE: MeasureItem, PaintItem and EraseItem are called repeatedly.
     * Therefore, it is critical for performance that these methods be as
     * efficient as possible.
     */
    Listener paintListener = new Listener() {
      public void handleEvent(Event event) {
        switch (event.type) {
        case SWT.MeasureItem: {
          Rectangle rect = image.getBounds();
          event.width += rect.width;
          event.height = Math.max(event.height, rect.height + 2);
          break;
        }
        case SWT.PaintItem: {
          int x = event.x + event.width;
          Rectangle rect = image.getBounds();
          int offset = Math.max(0, (event.height - rect.height) / 2);
          event.gc.drawImage(image, x, event.y + offset);
          break;
        }
        }
      }
    };
    tree.addListener(SWT.MeasureItem, paintListener);
    tree.addListener(SWT.PaintItem, paintListener);

    for (int i = 0; i < columnCount; i++) {
      tree.getColumn(i).pack();
    }
    shell.setSize(500, 200);
    shell.open();
    AtomicBoolean b = new AtomicBoolean(true);
    AtomicInteger i = new AtomicInteger(0);
    Thread t = new Thread(new Runnable() {
		@Override
		public void run() {
			while (b.get()) {
				if (!display.isDisposed()) {
					display.asyncExec(new Runnable() {
						@Override
						public void run() {
							if (!display.isDisposed()) {
								int index = i.getAndIncrement();
								TreeItem i0 = items.get((index + 0) % items.size());
								tree.deselect(i0);
								TreeItem i1 = items.get((index + 1) % items.size());
								tree.select(i1);
							}
						}
					});
				}
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
				}
			}
		}
    });
    t.start();
    while (!shell.isDisposed()) {
      if (!display.readAndDispatch())
        display.sleep();
    }
    b.set(false);
    if (image != null)
      image.dispose();
    display.dispose();
  }
}

(essentially this snippet, changed to continually select elements in the tree, which causes the memory leak: http://www.java2s.com/Tutorial/Java/0280__SWT/AddIcontoTreeItem.htm)

valgrind reports the memory leak e.g. as:

==94589== 12,540 (10,032 direct, 2,508 indirect) bytes in 627 blocks are definitely lost in loss record 302 of 14,499
==94589==    at 0x4C2B017: malloc (vg_replace_malloc.c:380)
==94589==    by 0x2A31F68D: g_malloc (in /usr/lib64/libglib-2.0.so.0.5600.1)
==94589==    by 0x2A336C8D: g_slice_alloc (in /usr/lib64/libglib-2.0.so.0.5600.1)
==94589==    by 0x26F7696D: gtk_tree_path_new (gtktreemodel.c:594)
==94589==    by 0x26F8733C: gtk_tree_store_get_path (gtktreestore.c:600)
==94589==    by 0x269E652A: Java_org_eclipse_swt_internal_gtk_GTK_gtk_1tree_1model_1get_1path (in /home/sandreev/git/eclipse/eclipse.platform.swt.binaries/bundles/org.eclipse.swt.gtk.linux.x86_64/libswt-pi3-gtk-4944r26.so)
==94589==    by 0x18FABB88: ???
==94589==    by 0x11BF802B: ???
==94589==    by 0x66FE052: JavaCalls::call_helper(JavaValue*, methodHandle const&, JavaCallArguments*, Thread*) (in /usr/lib/jvm/java-11-openjdk-11.0.11.0.9-1.el7_9.x86_64/lib/server/libjvm.so)
==94589==    by 0x676910C: jni_invoke_static(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, Thread*) [clone .isra.165] (in /usr/lib/jvm/java-11-openjdk-11.0.11.0.9-1.el7_9.x86_64/lib/server/libjvm.so)
==94589==    by 0x6773EFB: jni_CallStaticLongMethodV (in /usr/lib/jvm/java-11-openjdk-11.0.11.0.9-1.el7_9.x86_64/lib/server/libjvm.so)
==94589==    by 0x26743376: callback (in /home/sandreev/git/eclipse/eclipse.platform.swt.binaries/bundles/org.eclipse.swt.gtk.linux.x86_64/libswt-gtk-4944r26.so)
==94589==    by 0x2674F15A: fn1_6 (in /home/sandreev/git/eclipse/eclipse.platform.swt.binaries/bundles/org.eclipse.swt.gtk.linux.x86_64/libswt-gtk-4944r26.so)
==94589==    by 0x26D79E1A: gtk_cell_renderer_render (gtkcellrenderer.c:826)
==94589==    by 0x26D703D1: render_cell (gtkcellarea.c:1147)
==94589==    by 0x26D72E7F: gtk_cell_area_box_foreach_alloc (gtkcellareabox.c:1291)
==94589==    by 0x26D6FF2A: gtk_cell_area_real_render (gtkcellarea.c:1189)
==94589==    by 0x26FA9949: _gtk_tree_view_column_cell_render (gtktreeviewcolumn.c:2909)
==94589==    by 0x26FA0A8B: gtk_tree_view_bin_draw (gtktreeview.c:5327)
==94589==    by 0x26FA15FC: draw_bin (gtktreeview.c:5608)
==94589==    by 0x26EC2AAB: _gtk_pixel_cache_repaint (gtkpixelcache.c:357)
==94589==    by 0x26EC2AAB: _gtk_pixel_cache_draw (gtkpixelcache.c:447)
==94589==    by 0x26F8CF27: gtk_tree_view_draw (gtktreeview.c:5651)
==94589==    by 0x26E66D24: _gtk_marshal_BOOLEAN__BOXED (gtkmarshalers.c:83)
==94589==    by 0x26FAF8FE: gtk_widget_draw_marshaller (gtkwidget.c:945)
==94589==    by 0x2A08D901: g_closure_invoke (in /usr/lib64/libgobject-2.0.so.0.5600.1)
==94589==    by 0x2A09FE1A: ??? (in /usr/lib64/libgobject-2.0.so.0.5600.1)
==94589==    by 0x2A0A7CDB: g_signal_emit_valist (in /usr/lib64/libgobject-2.0.so.0.5600.1)
==94589==    by 0x2A0A82DE: g_signal_emit (in /usr/lib64/libgobject-2.0.so.0.5600.1)
==94589==    by 0x26FBA7F9: gtk_widget_draw_internal (gtkwidget.c:7025)
==94589==    by 0x26D9FFD3: gtk_container_propagate_draw (gtkcontainer.c:3841)

The problem seems to be in this code:

				// Account for the expander arrow offset in x position
				if (GTK.gtk_tree_view_get_expander_column (handle) == columnHandle) {
					/* indent */
					GdkRectangle rect3 = new GdkRectangle ();
					GTK.gtk_widget_realize (handle);
					path = GTK.gtk_tree_model_get_path (modelHandle, iter);
					GTK.gtk_tree_view_get_cell_area (handle, path, columnHandle, rect3);
					contentX[0] += rect3.x;
				}

Adding a call "GTK.gtk_tree_path_free (path)" fixes the leak.
Comment 1 Eclipse Genie CLA 2021-05-17 03:58:25 EDT
New Gerrit change created: https://git.eclipse.org/r/c/platform/eclipse.platform.swt/+/180664