Hi! I'm working on an Angular app that needs to display maps in different components. When each component is creating its own map after couple of switching between components displaying maps the browser memory started to increase until at some point completely crushes. This is especially easy to replicate on Chrome Dev Tools are opened. My question is what is proper way of releasing the map and all its resources (layers, graphics) in order to avoid the huge memory leak?
Hi @RazvanVxMt take a look at this discussion: https://github.com/Esri/jsapi-resources/issues/437#issuecomment-1235628447. It's about React, but the concept for managing the View and just removing the container is the same for Angular or any other framework.
If you still need further assistance, you'll need to provide a simple, working github repo, codesandbox or stackblitz example.
Hi @AndyGup. Thank you for your answer. We are already using this approach which drastically decreased the probability of memory leak however, there are still instances when memory leak appears. Another side effect of just replacing the container is that sometimes the map view does not redraw unless a zoom / pan is performed. I could not find anything in the docs about how a map view refresh can be triggered programmatically. Any hint on this?
Good Day
I've had a lot of problems with the memory and Angular, so this is how we keep it in check:
1. Use a Store to keep the view / map, and use the RenderFactory to attach the view / map into and remove it from a container.
2. Make sure you're removing, freeing or deleting as many resources as possible, for instance here is some code from our ngOnDestroy()
public ngOnDestroy(): void {
this.esriSvc.removeMapViewContainer(this.mapViewEl);
try {
this._Attribution?.destroy();
this._Attribution = null;
} catch (error) {
console.log(error);
}
try {
this._clickEvent?.remove();
this._clickEvent = null;
this._BasemapGalleryWatcher?.remove();
this._BasemapGalleryWatcher = null
} catch (error) {
console.log(error);
}
try {
this._sketchWidget?.destroy();
this._sketchWidget = null;
this.sketchLayer?.destroy();
this.sketchLayer = null;
} catch (error) {
console.log(error);
}
try {
this._view.map.layers.forEach((layer) => {
layer.clear()
layer.destroy()
})
this._view.map.layers = null;
} catch (error) {
console.log(error);
}
try {
this._view.ui.empty();
this._view.popup = null;
} catch (error) {
console.log(error);
}
try {
this._map.removeAll();
} catch (error) {
console.log(error);
}
try {
this._widgets.forEach((widget) => {
if (widget) {
try {
widget = null;
} catch (error) {
console.log(error);
}
}
})
this._widgets = [];
} catch (error) {
console.log(error);
}
//
// NOTE: This function resets every variable we allocate
//
this.destroyStuff();
}
3. Litsten to Andy, he's awesome and has helped me in the past.
Using the method I described, I've been able to cycle between 10k maps with essentially 0 memory increase. You can also look into using a global render setting, if that's applicable, and try to never destroy the map or remove and add it if you don't have to. I've found in a lot of cases it's better to just hide it, then detach and add it back. Although I know that can't always be avoided.