branflake2267 / GWT-Maps-V3-Api

GWT Maps V3 Javascript Bindings
Other
144 stars 113 forks source link

Memory Leak - adding and removing map #168

Closed wikiro closed 10 years ago

wikiro commented 11 years ago

I create simple project. Button and map. Compiled it.

When i press button - map recreated and added to RootPanel. Browser (opera 11+, ) memory increase + 4 mb every time (if map is very big, 10+ mb).

Here the code:

If i add some polylines and overlay memory increases + 20 mb

on module load

RootPanel.get("right").add(new CommonMap(1,1, 18, 1));

        CommonButton b = new CommonButton("reload");

        b.addClickListener(new CommonButtonClickListener() {
            @Override
            public void onClick() {
                RootPanel.get("right").clear();
                CommonMap map = new CommonMap(1,1, 18, 1);
                map.addStyleName("map");
                RootPanel.get("right").add(map);

            }
        });
        RootPanel.get("left").add(b);
public class CommonMap extends FlowPanel implements MapPanelInterface {
    private MapWidget mapWidget;
    private LatLng currentCoordinate;
    private MapCanvasProjection projection;

    public CommonMap(double lat, double lon, int zoom, int mapTypeId) {
        MapTypeControlOptions controlOptions = MapTypeControlOptions.newInstance();
        controlOptions.setMapTypeIds(new String[] { "CloudMade", "MapQuest", "OSM", MapTypeId.ROADMAP.toString(), MapTypeId.HYBRID.toString(), MapTypeId.SATELLITE.toString(), MapTypeId.TERRAIN.toString()});

        setStyleName("commonMap");
        MapOptions options = MapOptions.newInstance();
        options.setZoom(zoom);
        options.setCenter(LatLng.newInstance(lat, lon));
        options.setMapTypeControlOptions(controlOptions);
        options.setScrollWheel(true);
        options.setMapTypeControl(true);
        options.setDisableDefaultUi(true);
        options.setStreetViewControl(false);
        options.setKeyboardShortcuts(false);
        options.setPanControl(false);
        ZoomControlOptions zoomControlOptions = ZoomControlOptions.newInstance();
        zoomControlOptions.setPosition(ControlPosition.RIGHT_CENTER);
        zoomControlOptions.setStyle(ZoomControlStyle.SMALL);
        options.setZoomControlOptions(zoomControlOptions);
        ImageMapType osmMapType = getOsmMapType();
        ImageMapType mapQuestType = getMapQuestType();
        ImageMapType cloudMade = getCloudMadeType();
        mapWidget = new MapWidget(options);
        mapWidget.addStyleName("map");
        mapWidget.getMapTypeRegistry().set("OSM", osmMapType);
        mapWidget.getMapTypeRegistry().set("MapQuest", mapQuestType);
        mapWidget.getMapTypeRegistry().set("CloudMade", cloudMade);
        add(mapWidget);

        if (mapTypeId == 2) {
            mapWidget.setMapTypeId("OSM");
        } else {
            mapWidget.setMapTypeId("MapQuest");
        }
        addMousePositionFetching();

    }

    private ImageMapType getOsmMapType() {
        ImageMapTypeOptions opts = ImageMapTypeOptions.newInstance();
        opts.setMaxZoom(18);
        opts.setMinZoom(1);
        opts.setName("OSM");
        opts.setTileSize(Size.newInstance(256, 256));
        opts.setTileUrl(new TileUrlCallBack() {
            public String getTileUrl(Point point, int zoom) {
                return "http://tile.openstreetmap.org/" + zoom + "/" + ((int) point.getX()) + "/" + ((int) point.getY()) + ".png";
            }
        });

        return ImageMapType.newInstance(opts);
    }

    private ImageMapType getMapQuestType() {
        ImageMapTypeOptions opts = ImageMapTypeOptions.newInstance();
        opts.setMaxZoom(18);
        opts.setMinZoom(1);
        opts.setName("MapQuest");
        opts.setTileSize(Size.newInstance(256, 256));
        opts.setTileUrl(new TileUrlCallBack() {
            public String getTileUrl(Point point, int zoom) {
                return "http://otile1.mqcdn.com/tiles/1.0.0/osm/" + zoom + "/" + ((int) point.getX()) + "/" + ((int) point.getY()) + ".png";
            }
        });

        return ImageMapType.newInstance(opts);
    }

    private ImageMapType getCloudMadeType() {
        ImageMapTypeOptions opts = ImageMapTypeOptions.newInstance();
        opts.setMaxZoom(18);
        opts.setMinZoom(1);
        opts.setName("CloudMade");
        opts.setTileSize(Size.newInstance(256, 256));
        opts.setTileUrl(new TileUrlCallBack() {
            public String getTileUrl(Point point, int zoom) {
                return "http://b.tile.cloudmade.com/339772fb4ce84f2ab0eb5e57bdbdb10b/997/256/" + zoom + "/" + ((int) point.getX()) + "/" + ((int) point.getY()) + ".png";
            }
        });

        return ImageMapType.newInstance(opts);
    }

    public void addDragHandler(DragMapHandler handler) {
        mapWidget.addDragHandler(handler);
    }

    public void addRightClickHandler(RightClickMapHandler handler) {
        mapWidget.addRightClickHandler(handler);
    }

    public void addDragEndHandler(DragEndMapHandler handler) {
        mapWidget.addDragEndHandler(handler);
    }

    public void addZoomChangeHandler(ZoomChangeMapHandler handler) {
        mapWidget.addZoomChangeHandler(handler);
    }

    public void addClickHandler(ClickMapHandler handler) {
        mapWidget.addClickHandler(handler);
    }

    public void addDragStartHandler(DragStartMapHandler handler) {
        mapWidget.addDragStartHandler(handler);
    }

    public void checkResize() {
        mapWidget.triggerResize();
    }

    public LatLngBoxBounds getBoxBounds() {
        return LatLngBoxBounds.createByBounds(mapWidget.getBounds());
    }

    @Override
    public LatLng getCurrentMouseMoveCoordinate() {
        return currentCoordinate;
    }

    public void fitBounds(SerializableCoordinate a, SerializableCoordinate b) {
        fitBounds(a, b, 0);
    }
    public void fitBounds(LatLngBounds bounds) {
        mapWidget.fitBounds(bounds);
    }

    public void scrollToCenter(LatLng c) {
        mapWidget.panTo(LatLng.newInstance(c.getLatitude(), c.getLongitude()));
    }

    @Override
    public int[] getCurrentMouseAbsolutePosition() {
        int xy[] = new int[2];
        xy[0] = mapWidget.getAbsoluteLeft() + (int) projection.fromLatLngToContainerPixel(currentCoordinate).getX();
        xy[1] = mapWidget.getAbsoluteTop() + (int) projection.fromLatLngToContainerPixel(currentCoordinate).getY();
        return xy;
    }

    public int[] getCurrentMouseAbsolutePosition(LatLng lng) {
        int xy[] = new int[2];
        xy[0] = mapWidget.getAbsoluteLeft() + (int) projection.fromLatLngToContainerPixel(lng).getX();
        xy[1] = mapWidget.getAbsoluteTop() + (int) projection.fromLatLngToContainerPixel(lng).getY();
        return xy;
    }

    public void removeOverlay(CommonMapOverlayInterface overlayInterface) {
        overlayInterface.getOverlay(mapWidget).setMap(null);
    }

    public void addOverlay(CommonMapOverlayInterface overlayInterface) {
        overlayInterface.getOverlay(mapWidget).setMap(mapWidget);
    }

    public void addOverlay(Marker overlayInterface) {
        overlayInterface.setMap(mapWidget);
    }

    public int getZoomLevel() {
        return mapWidget.getZoom();
    }

    private void addMousePositionFetching() {
        GlobalOverlayNothingHandler global = new GlobalOverlayNothingHandler();
        OverlayView globalOverlay = OverlayView.newInstance(mapWidget, global, global, global);
        mapWidget.addMouseMoveHandler(new MouseMoveMapHandler() {
            public void onEvent(MouseMoveMapEvent event) {
                currentCoordinate = event.getMouseEvent().getLatLng();
            }
        });
    }

    class GlobalOverlayNothingHandler implements OverlayViewOnDrawHandler, OverlayViewOnAddHandler, OverlayViewOnRemoveHandler {
        public void onAdd(OverlayViewMethods methods) { projection = methods.getProjection(); }
        public void onDraw(OverlayViewMethods methods) {}
        public void onRemove(OverlayViewMethods methods) {}
    }
}
branflake2267 commented 11 years ago

Good find. Thanks for reporting. Did you notice which things were getting left behind? Ill check the detaching...

branflake2267 commented 11 years ago

I'm run out of maps api coding time today. The next goal is to trace the dom and see whats getting left behind.

wikiro commented 11 years ago

wikiro commented 11 years ago

~30 reloads

look at created objects:

branflake2267 commented 11 years ago

The idea is to see if its possible to search for the orphaned elements. But I forgot to mention its a challenge to find the elements unless compiled with pretty.

branflake2267 commented 11 years ago

By the way, good job. Do you think you could compile with pretty and then see if you can find the orphaned elements? I haven't had time to look and it won't be late today until I get another chance. Check detached that are red.

wikiro commented 11 years ago

http://stackoverflow.com/questions/12711330/google-maps-js-api-v3-recreate-map-with-no-memory-leak-move-map-object-to-dif

branflake2267 commented 11 years ago

Interesting. Seems it might be upstream. I've been thinking about this, but haven't had much time to test. I wonder do you think you could reuse the map widget?

wikiro commented 11 years ago

http://code.google.com/p/gmaps-api-issues/issues/detail?can=2&start=0&num=100&q=memory&colspec=ID%20Type%20Status%20Introduced%20Fixed%20Summary%20Stars%20ApiType%20Internal&groupby=&sort=&id=3803

branflake2267 commented 11 years ago

Starred the issue

twistedpair commented 11 years ago

Through my star on there too. Quite surprising that there is no way to clean these up. Usually the bar is higher for Google's APIs.

branflake2267 commented 11 years ago

Good point. I was wondering too.

twistedpair commented 11 years ago

Should this bug be closed or reprioritized given that it's up to the Google Maps team to fix it?

branflake2267 commented 11 years ago

I was thinking about that too. I think we could close it b/c it looks like we can do nothing about it at the moment, but on the other hand I'd like to do something about it but sure at the moment about the options.

branflake2267 commented 11 years ago

I think you're right, set a new tag on it for later, so we can march forward with 3.11. Maybe one of the users will suggest a work around when they find this pattern.

I'm at Sencha in Redwood city until this wednesday, so I've lost a little productivity on the open source projects I like to work on :)

twistedpair commented 11 years ago

@branflake2267 nice. I hope you make some friends there.

twistedpair commented 10 years ago

Closing because this is an upstream bug in the JS API. FYI, I use GWTP and a single map page that can be marshaled between views. This way the map loads instantly as it was never unloaded and you don't have the memory leaks from multiple maps in your app.