Baidu map memory leak problem
In a Baidu map, we would see the page crash as memory usage kept rising while we were creating markers in a loop. Or the page may be jammed due to a large amount of data and high CPU usage. As shown in figure:
Examples of requirements:
Use Baidu map to display the location of vehicles, and refresh the location information once for 30 seconds. In the face of such needs, the position of vehicles should be displayed. Each vehicle needs a Marker point, and the aggregation of vehicles should be considered. The code is as follows:
const clustererInstance = null; const mapInstance = null; // Initialize the map... // Create Marker pointsfunctionAddMarker (data) {/ / clear the overlay on the map before you create mapInstance. ClearOverlays (); const markers = [];for (let i = 0, len = data.length; i < len; i++) {
const item = data[i];
const point = new BMap.Point(item.long, item.lat);
const marker = new BMap.Marker(point);
markers.push(marker);
}
if (clustererInstance) {
clustererInstance.clearMarkers();
clustererInstance.addMarkers(totalMarkers);
} else{ clustererInstance = new BMapLib.MarkerClusterer( mapInstance, { markers: markers }); }}Copy the code
This is just to implement the creation of marker points, timers and data requests are not shown. The addMarker method is called once each time the loop requests the data, which can complete the display of vehicle points.
Problem 1: When the amount of data was small, there would be no obvious problems in such implementation. However, when the amount of data reached tens of thousands of times or more, although clearOverlays and clearMarkers were cleared each time when the page was cycled to create Marker, baidu’s two methods did not achieve memory release. Each time the browser loops, the memory grows until the page crashes.
Solutions:
When faced with this problem, we create marker points in the first cycle and store them with vue’s store, and only modify marker postion in the next cycle. The code is as follows:
import { mapState } from 'vuex'; const clustererInstance = null; const mapInstance = null; computed: { ... mapState(['allMcMarkers'}, methods: {// initialize the mapinitMap() {... } / / create Marker point addMarker (data) {/ / clear the overlay on the map before you create mapInstance. ClearOverlays (); const markers = [];for (leti = 0, len = data.length; i < len; i++) { const item = data[i]; const marker = null; const point = new BMap.Point(item.long, item.lat); // Determine whether there is a marker, if there is, directly replace the positionif (this.allMcMarkers[i]) {
marker = this.allMcMarkers[i];
marker.setPosition(point);
} else {
marker = new BMap.Marker(point);
this.allMcMarkers.push(marker);
}
markers.push(marker);
}
if (clustererInstance) {
clustererInstance.clearMarkers();
clustererInstance.addMarkers(totalMarkers);
} else{ clustererInstance = new BMapLib.MarkerClusterer( mapInstance, { markers: markers }); }}}Copy the code
Problem two: when the amount of data is too large, the front-end page rendering memory will be very large. It can also cause the page to freeze. So we can consider batch rendering. Batch rendering can be implemented using setInterval or setTimeout, requestAnimationFrame.
SetTimeout implements batch rendering
import { mapState } from 'vuex'; const clustererInstance = null; const mapInstance = null; computed: { ... mapState(['allMcMarkers'}, methods: {var result = []; var result = []; const _l = Math.ceil(data.length / 5000);for (var i = 0; i < _l; i++) {
result.push(data.slice(i * 5000, (i + 1) * 5000));
}
return result;
},
batchRender (data) {
return new Promise((resolve, reject) => {
var groups = this.group(data);
let totalMarkers = [];
const that = this;
this.renderMarkersLoading = true;
const zoom = 5;
for (leti = 0; i < groups.length; I ++) {// closure to keep I correct // eslint-disable-next-line wrap-iife window.setTimeout(function () {
var group = groups[i];
var index = i;
return function() {// Batch render totalMarkers = totalMarkers. Concat (th.addmarker (group, zoom, index));if (totalMarkers.length === data.length) {
that.createMarker(totalMarkers);
that.renderMarkersLoading = false; }}; } (), 1); } resolve(); }); } / / create Marker point addMarker (data) {/ / clear the overlay on the map before you create mapInstance. ClearOverlays (); const markers = [];for (leti = 0, len = data.length; i < len; i++) { const item = data[i]; const marker = null; const point = new BMap.Point(item.long, item.lat); // Determine whether there is a marker, if there is, directly replace the positionif (this.allMcMarkers[i]) {
marker = this.allMcMarkers[i];
marker.setPosition(point);
} else {
marker = new BMap.Marker(point);
this.allMcMarkers.push(marker);
}
markers.push(marker);
}
return markers;
},
createMarker (totalMarkers) {
if (totalMarkers.length) {
if (clustererInstance) {
clustererInstance.clearMarkers();
clustererInstance.addMarkers(totalMarkers);
} else{ clustererInstance = new BMapLib.MarkerClusterer(this.map, { markers: totalMarkers }); }}}},Copy the code
We also choose requestAnimationFrame to implement batch rendering when implementing animation rendering. The above solution may not be optimal, if you have a better solution, please leave a comment.