Status Update
Comments
ev...@gmail.com <ev...@gmail.com> #2
ev...@gmail.com <ev...@gmail.com> #3
li...@gmail.com <li...@gmail.com> #4
da...@gmail.com <da...@gmail.com> #5
Every time the map is created and destroyed it leaves around 1000 DOM node elements in memory, even if the map is in an iframe and that iframe is destroyed.
dk...@morfunk.com <dk...@morfunk.com> #6
jk...@gmail.com <jk...@gmail.com> #7
ac...@gmail.com <ac...@gmail.com> #8
If there will be no ffx, is there an official workaround? (I am also developing a single page web app).
yy...@gmail.com <yy...@gmail.com> #10
google.maps.event.clearInstanceListeners(window);
google.maps.event.clearInstanceListeners(document);
google.maps.event.clearInstanceListeners(mapDiv);
yy...@gmail.com <yy...@gmail.com> #11
google.maps.event.clearInstanceListeners(window);
google.maps.event.clearInstanceListeners(document);
google.maps.event.clearInstanceListeners(mapDiv);
yy...@gmail.com <yy...@gmail.com> #12
It seems that in "common", gmap attach Listener for "resize"/"scroll" directly by window.addeventlistener/window.attachevent(NOT google.maps.event.addDomListener), and the Closure saves too too much vars in its context.
yy...@gmail.com <yy...@gmail.com> #13
ni...@eagle.io <ni...@eagle.io> #14
jk...@gmail.com <jk...@gmail.com> #15
I just remove it from the DOM and re-add it to a different section.
All my pages implement a dispose method that cleans the map up to it's original state, i.e. remove all markers polygons, restore map options etc.
After moving the map element make sure you fire the 'resize' event so maps redraws itself at the correct size.
[Deleted User] <[Deleted User]> #16
me...@gmail.com <me...@gmail.com> #17
And we have a lot of problems because of that. On one page we need to put 10 photospheres based on google maps API, and it consumes too much RAM (about 60MB per ONE photosphere, which equal more than 600MB for this site).
We've been trying some solutions:
Do you sth we can correct to minimize RAM consumption?
And dear Google, can you finally update this issue?
bi...@gmail.com <bi...@gmail.com> #18
em...@gmail.com <em...@gmail.com> #19
re...@gmail.com <re...@gmail.com> #20
re...@gmail.com <re...@gmail.com> #21
si...@gmail.com <si...@gmail.com> #22
ff...@gmail.com <ff...@gmail.com> #23
ri...@googlemail.com <ri...@googlemail.com> #24
ch...@gmail.com <ch...@gmail.com> #25
jk...@gmail.com <jk...@gmail.com> #26
ro...@gmail.com <ro...@gmail.com> #27
kd...@gmail.com <kd...@gmail.com> #28
re...@gmail.com <re...@gmail.com> #29
ck...@gmail.com <ck...@gmail.com> #30
Leaflet's API patterns are similar to the Google's, so the switch wasn't too difficult, and I was much happier with the result.
re...@gmail.com <re...@gmail.com> #31
st...@google.com <st...@google.com> #32
First, apologies that we've been silent on this issue up to now.
Next, there's an easy work around, which is to reuse your Map instances. If there's a good reason why you can't do this I'd love to know.
I'm labeling this as won't fix, because this is technically difficult. We're not really sure in how many places we're leaking :-(
Additionally, we'd probably need to introduce some sort of map.destroy() method. Otherwise, how are we to know whether you're planning to reuse the map? Javascript doesn't have destructors, so we can't know you've thrown away your reference to the object.
Sorry to be the bearer of bad news.
ch...@missouristate.edu <ch...@missouristate.edu> #33
However, in multiple cases this technique required significant refactoring and much more complex code to achieve.
st...@google.com <st...@google.com> #34
Was it complicated because you have to reuse the map div? Or some other reason?
ch...@missouristate.edu <ch...@missouristate.edu> #35
ck...@gmail.com <ck...@gmail.com> #36
I suppose I could have maintained a pool of reusable map instances, but that sounds like a bad idea, especially considering that there are alternative mapping frameworks out there that *do* support a map.destroy() that makes it easy to create/dispose instances.
(I'm not hating on Google here. It's perfectly acceptable that Google's API wasn't designed for my use case)
ma...@gmail.com <ma...@gmail.com> #37
wi...@gmail.com <wi...@gmail.com> #38
Reason for destroying instance is due to need of change language in single-page-aplication.
As far as I know, only way to change language is re-instatiate instance of Google Maps.
Is this reason enough?
zw...@google.com <zw...@google.com> #39
The iframe solution mentioned should work, as long as the bootstrap is also loaded in that iframe. That also allows you to switch language, as you can destroy the iframe, recreate a new one, reload a new bootstrap with a new language in the new iframe.
mo...@gmail.com <mo...@gmail.com> #40
More over whole app can open multiple instances of specific view (edit different user) which has map. This leads to taking 30MB for each view, which is latter not freed. Only solution is rewrite every part of app which is using map to single view instance. We have 2015 not 1995, and multi instance view are very common.
di...@gmail.com <di...@gmail.com> #41
ma...@gmail.com <ma...@gmail.com> #42
mu...@gmail.com <mu...@gmail.com> #43
jw...@gmail.com <jw...@gmail.com> #44
google.maps.event.trigger(maphandle,'resize');
mu...@gmail.com <mu...@gmail.com> #45
mu...@gmail.com <mu...@gmail.com> #46
cheers
jw...@gmail.com <jw...@gmail.com> #47
pr...@gmail.com <pr...@gmail.com> #48
Any sample example would be recommeneded for reusability of map Instance
pr...@gmail.com <pr...@gmail.com> #49
al...@gmail.com <al...@gmail.com> #50
ni...@gmail.com <ni...@gmail.com> #51
* window 'scroll' listener
* window 'blur' listener
* Also stored in window.__e3_.blur
* window 'resize' listener
* Most stored in property window.__e3_.resize
* One is *not* stored in window.__e3_.resize!
* document 'click' listener
* Also stored in property document.__e3_.click
* document 'keydown' listener
* Also stored in property document.__e3_.keydown
* document 'keypress' listener
* Also stored in document.__e3_.keypress
* document 'keyup' listener
* Also stored in property document.__e3_.keyup
* document 'MSFullscreenChange' listener
* Also stored in property document.__e3_.MSFullscreenChange
* document 'fullscreenchange' listener
* Also stored in property document.__e3_.fullscreenchange
* document 'mozfullscreenchange' listener
* Also stored in property document.__e3_.mozfullscreenchange
* document 'webkitfullscreenchange' listener
* Also stored in property document.__e3_.webkitfullscreenchange
Once all of those are cleared, the map is freed. Miss any one of those, and the map is retained. The 'scroll' and 'resize' listeners are the most tricky. Without changing the source code of Google Maps, you'll have to shim addEventListener so you can stash a reference to the listeners for removal later.
vi...@gmail.com <vi...@gmail.com> #52
ni...@gmail.com <ni...@gmail.com> #53
I do, but it's somewhat troublesome! The following should remove all listeners for all maps on the webpage. Removing one map of many on a webpage would be more complicated, and may require Google to actually fix this memory leak with a proper 'destroy' method. :(
By the way, if Google is listening, I can provide stack traces into the minified library that point to the exact source code locations that add these listeners. Just let me know! These traces won't be useful to people using the Maps SDK, since we can't change its code.
Note that if Google updates the Google Maps SDK, then the __e3_ property may be named something different and you will need to adjust the code -- __e3_ seems to be an autogenerated name during minification.
You should double check that the following code fixes the leak. I haven't rigorously tested it yet -- I used a slightly different fix in my own testing for scientific purposes that doesn't make sense in a production environment. (I found & diagnosed this memory leak automatically using a tool I developed.)
// Helper function: Removes all event listeners registered with Google's addDomListener function,
// including from __e3_ properties on target objects.
function removeAllGoogleListeners(target, event) {
var listeners = target['__e3_'];
if (!listeners) {
console.warn("Couldn't find property __e3_ containing Google Maps listeners. Perhaps Google updated the Maps SDK?");
return;
}
var evListeners = listeners[event];
if (evListeners) {
for (var key in evListeners) {
if (evListeners.hasOwnProperty(key)) {
google.maps.event.removeListener(evListeners[key]);
}
}
}
}
// Removes all DOM listeners for the given target and event.
function removeAllDOMListeners(target, event) {
var listeners = target['__listeners_'];
if (!listeners) { return; }
// Copy to avoid iterating over array that we mutate via removeEventListener
var copy = listeners.slice(0);
for (var i = 0; i < copy.length; i++) {
target.removeEventListener(event, copy[i]);
}
}
// Shim addEventListener to capture and store registered event listeners.
var addEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function() {
var eventName = arguments[0];
var listener = arguments[1];
if (!this['__listeners_']) {
this.__listeners_ = {};
}
var listeners = this.__listeners_;
if (!listeners[eventName]) {
listeners[eventName] = [];
}
listeners[eventName].push(listener);
return addEventListener.apply(this, arguments);
};
var removeEventListener = EventTarget.prototype.removeEventListener;
EventTarget.prototype.removeEventListener = function() {
var eventName = arguments[0];
var listener = arguments[1];
if (this['__listeners_'] && this.__listeners_[eventName]) {
// Loop because the same listener may be added twice with different
// options, and because our simple addEventListener shim doesn't
// check for duplicates.
while (true) {
var i = this.__listeners_[eventName].indexOf(listener);
if (i === -1) {
break;
}
this.__listeners_[eventName].splice(i, 1);
}
}
return removeEventListener.apply(this, arguments);
};
// After you remove the Google Map from the DOM, call this function to completely free the object.
function destroyGoogleMaps() {
removeAllGoogleListeners(window, 'blur');
removeAllGoogleListeners(window, 'resize');
removeAllGoogleListeners(document, 'click');
removeAllGoogleListeners(document, 'keydown');
removeAllGoogleListeners(document, 'keypress');
removeAllGoogleListeners(document, 'keyup');
removeAllGoogleListeners(document, 'MSFullscreenChange');
removeAllGoogleListeners(document, 'fullscreenchange');
removeAllGoogleListeners(document, 'mozfullscreenchange');
removeAllGoogleListeners(document, 'webkitfullscreenchange');
// ASSUMPTION: No other library registers global resize and scroll event listeners! If this is not true, then you'll need to add logic to avoid removing these.
removeAllDOMListeners(window, 'resize');
removeAllDOMListeners(window, 'scroll');
}
Hope that helps! Let me know if it gives you trouble or misses something.
jp...@gmail.com <jp...@gmail.com> #54
ni...@gmail.com <ni...@gmail.com> #55
I think this bug is now feasible to fix given all of the information supplied, so long as Google is willing to add a `dispose()` method to the API.
jp...@gmail.com <jp...@gmail.com> #56
[Deleted User] <[Deleted User]> #57
[Deleted User] <[Deleted User]> #58
[Deleted User] <[Deleted User]> #59
ch...@gmail.com <ch...@gmail.com> #60
null is not an object (evaluating 'a.scale')
th...@gmail.com <th...@gmail.com> #61
Having in mind modern SPAs, where map could be used only on one small "contacts" page - thats unbearable.
ke...@gmail.com <ke...@gmail.com> #62
Also Google: "nah, maps shouldn't support any of that. It needs to work in IE6, nobody cares about cool stuff in Google Chrome 71"
No offence meant, but Chrome has moved the goalposts and you have to revisit this in 2019.
Addressing the reasons you've given why this is _Won't Fix_
> "Next, there's an easy work around, which is to reuse your Map instances. If there's a good reason why you can't do this I'd love to know."
I'm using web components and shadow DOM - they don't mean I _can't_ do this, but they make reusing chunks of connected up DOM like this an absolute pain. Additional bugs (mostly thanks to Maps API assuming that any CSS it adds to the document <head> will apply, like #122064478) mean the shadow DOM components that the map's parent element is being moved between all need to apply various bug workaround styles.
This bug significantly increases my maintenance costs for using the (already expensive) maps. Being able to workaround a bug doesn't make it OK to leave that bug unresolved for 8 years.
> "I'm labeling this as won't fix, because this is technically difficult. We're not really sure in how many places we're leaking :-("
That's... worrying. Also: you're Google, and this API is making you many millions, are you seriously saying your technical debt is so expensive that you can't fix it?
> "Additionally, we'd probably need to introduce some sort of map.destroy() method. Otherwise, how are we to know whether you're planning to reuse the map?"
Yup, that's what I'd expect as a bare minimum. Do that.
> "Javascript doesn't have destructors, so we can't know you've thrown away your reference to the object. "
Web components do. Even if they didn't we could tidy this up on routing SPA calls if we had a `map.destroy()` method of some kind.
You have some amazing developers and developer advocates who have build amazing things with the modern web tech like PWAs, web components and background workers, maybe get some of them to look at this problem?
ak...@gmail.com <ak...@gmail.com> #63
Ma...@synesis.ru <Ma...@synesis.ru> #64
You say "What the problem to reuse map instrances?"
We answer "What the problem collect all the listeners and clear them on destroy?" Refactor once, write unit|e2e test to check memory leak after destroy, and safe live to millions
Eventually We pay you for usage of this map
jh...@google.com <jh...@google.com> #65
po...@gmail.com <po...@gmail.com> #66
Yes, I have noticed that google maps for longer period have no memory leaks but recently I added just two lines into my project...
var trafficLayer = new google.maps.TrafficLayer();
trafficLayer.setMap(map);
... and there is memory leak again! So I can not use traffic layer feature because of memory leaks are not acceptable in our project. Can you please confirm that using traffic layer causes memory leak? Could you fix it please?
UPDATE: I have tested it little more and we have to use trafficLayer.setMap(null) to avoid memory leak, but doc does not warn about it and it is strange because e.g. Marker we do not have to reset using .setMap(null) and it does not cause memory leak. Also doc
ch...@google.com <ch...@google.com> #67
Thanks!
sh...@ubimo.com <sh...@ubimo.com> #68
ja...@gmail.com <ja...@gmail.com> #69
ci...@gmail.com <ci...@gmail.com> #70
re...@gmail.com <re...@gmail.com> #71
Well... here...we....are...
"13 years later. Rip"
Description
I created a test page
Each new instance would use about 2 MB more memory. Running the Google Chrome profiler, I observed that after destroying the map, there would still be detached DOM nodes, yet I don't see how I'm preserving a reference to them.