I suggested in the get-google-api Overview, I'm following up here with an 
issue  created.

Platform information: OS X 10.9, Chrome 31.0.1650.57, GWT 2.5.1.  Unknown if 
this problem exists on other platforms.

After adding a PieChart to a widget (for example, a VerticalPanel) and then 
removing the PieChart (using HasWidgets.remove(widget),
the PieChart no longer appears within the enclosing widget as expected.

However, when I check Chrome Dev Tools Containment view, I can see that 
elements for the PieChart remain behind in a detached DOM
tree, instead of being garbage collected.


I followed 
up<https://groups.google.com/forum/#%21topic/Google-Web-Toolkit/HcbLxVn1M6Q>with
 the GWT group first.  The only responder suggested that this DOM leak 
is entirely within the core visualization code, and not specific to GWT.  
Read the post for details on how we arrived at this conclusion.

I've attached a stand-alone class that demonstrates the issue.  To 
reproduce:
1. Deploy app in Prod mode
2. Open the app in Chrome
3. Click the "Add Chart" button and wait for chart to appear
4. Click the "Remove Chart: button
5. Using Chrome's dev tools, take a heap snapshot
6. Open the heap snapshot in the Containment view.  You should see elements 
in the detached DOM tree, similar to the screenshot:

<https://lh3.googleusercontent.com/-sVRJt4cmMdk/Up9-TH7Lr3I/AAAAAAAAABY/ZSnF3B9Y53U/s1600/Screen+Shot+2013-12-04+at+11.04.40+AM.png>
Although the names for the detached element objects (or the references to 
them) are obscured, even when compiled PRETTY or DETAILED, a look inside 
them reveals that they are related to gwt-viz.

Am I not removing the PieChart correctly, or is there a DOM/memory leak 
here?

-- 
http://groups.google.com/group/Google-Web-Toolkit-Contributors
--- 
You received this message because you are subscribed to the Google Groups "GWT 
Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to google-web-toolkit-contributors+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
package demo.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.visualization.client.AbstractDataTable;
import com.google.gwt.visualization.client.DataTable;
import com.google.gwt.visualization.client.VisualizationUtils;
import com.google.gwt.visualization.client.AbstractDataTable.ColumnType;
import com.google.gwt.visualization.client.visualizations.corechart.Options;
import com.google.gwt.visualization.client.visualizations.corechart.PieChart;
//import com.smartgwt.client.widgets.calendar.Timeline;

/**
 * This is a demo of how to properly handle dynamically created and destroyed
 * widgets. Entry point classes define <code>onModuleLoad()</code>.
 */
public class WidgetAddDelete implements EntryPoint {

    private final Button addChartButton = new Button("Add PieChart");
    private final Button removeChartButton = new Button("Remove PieChart");
//    private final Button addTimelineButton = new Button("Add Timeline");
//    private final Button removeTimelineButton = new Button("Remove Timeline");

    private final VerticalPanel mainPanel = new VerticalPanel();

    private Widget contentWidget = null;
//    private Widget timelineWidget = null;

    /**
     * This is the entry point method.
     */
    public void onModuleLoad() {
        mainPanel.add(addChartButton);
        mainPanel.add(removeChartButton);
//        mainPanel.add(addTimelineButton);
//        mainPanel.add(removeTimelineButton);

        RootPanel.get().add(mainPanel);

        setUpButtons();
    }

    /**
     * Add click handlers to the buttons.
     */
    private void setUpButtons() {
        addChartButton.addClickHandler(new ClickHandler() {
            public void onClick(final ClickEvent event) {
                if (contentWidget == null) {
                    Runnable onLoadCallback = new Runnable() {
                        public void run() {
                            PieChart content = new PieChart(createTable(), createOptions());
                            mainPanel.add(content);
                            contentWidget = content;
                        }
                    };
                    VisualizationUtils.loadVisualizationApi(onLoadCallback, PieChart.PACKAGE);
                }
            }
        });
        removeChartButton.addClickHandler(new ClickHandler() {
            public void onClick(final ClickEvent event) {
                if (contentWidget != null) {
                    mainPanel.remove(contentWidget);
                    contentWidget = null;
                }
            }
        });
//        addTimelineButton.addClickHandler(new ClickHandler() {
//            public void onClick(final ClickEvent event) {
//                if (timelineWidget == null) {
//                    Timeline timeline = new Timeline();
//                    mainPanel.add(timeline);
//                    timelineWidget = timeline;
//                }
//            }
//        });
//        removeTimelineButton.addClickHandler(new ClickHandler() {
//            public void onClick(final ClickEvent event) {
//                if (timelineWidget != null) {
//                    timelineWidget.removeFromParent();
//                    mainPanel.remove(timelineWidget);
//                    timelineWidget = null;
//                }
//            }
//        });
    }

    private AbstractDataTable createTable() {
        DataTable data = DataTable.create();
        data.addColumn(ColumnType.STRING, "Task");
        data.addColumn(ColumnType.NUMBER, "Hours per Day");
        data.addRows(2);
        data.setValue(0, 0, "Work");
        data.setValue(0, 1, 14);
        data.setValue(1, 0, "Sleep");
        data.setValue(1, 1, 10);
        return data;
    }

    private Options createOptions() {
        Options options = Options.create();
        options.setWidth(400);
        options.setHeight(240);
        options.setTitle("My Daily Activities");
        return options;
    }
}

Reply via email to