I have been experiencing Javascript Out of Memory errors with a Web App that loads a Google Map and continuously pans from one point to the other. It takes about a half day before it runs out of memory, but I would really like it to last far longer. I have identified that the memory leak occurs when the map.panTo method is used, although I very much suspect that it could be the way I'm using it (Javascript is not my strong suit). Can you please look at this code and help me fix this memory leak? I have sped up the interval that it pans in this demo code for sake of time during debugging (the leak is obvious even by checking the process in Windows Task Manager). This runs off of ASP .Net 3.5 Web Forms, but there are no PostBacks and the code is completely HTML, CSS, and Javascript. It leaks the worst in IE (which is what I need to use to display this).
Edit:
- I have tried using different versions of the Google Maps API (2, 3.0, and 3.6).
- I know that I don't need to put the map in the example code into a Hashtable or Array, but that doesn't affect the memory leak anyways.
- I'm trying to avoid a hack, such as refreshing the page every so often or using the static Google Maps API.
- Bounty will go to whoever figures out how to fix the memory leak or whoever clearly identifies and proves why it can't be fixed (if that is the case).
Code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Map Shi开发者_开发技巧fter</title>
<style type="text/css">
#divMap { height: 400px; width:400px; position:absolute; top:10%; left:10%; border: 2px solid #ff1100; }
</style>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?v=2&sensor=false"></script>
<script type="text/javascript" language="javascript">
//Have tried both HashTable and Array (Both Leak)...
var m_arrMaps = new HashTable(); //new Array();
var m_Timer;
function HashTable() {
return this;
}
function LoadMapAndStartShifting() {
var objMapCenterPoint = new google.maps.LatLng(20.5, -156);
var objMapOptions = { zoom: 9, center: objMapCenterPoint, mapTypeId: google.maps.MapTypeId.ROADMAP,
scaleControl: false, streetViewControl:false, zoomControl: false, mapTypeControl: false, panControl: false
}
var map = new google.maps.Map(document.getElementById("divMap"), objMapOptions);
m_arrMaps["ShiftingMap"] = map;
setTimeout("ShiftMap(20.5, -156);", 700);
}
function ShiftMap(decLat, decLng) {
var objLatLong = new google.maps.LatLng(decLat, decLng);
//Have tried setCenter instead of preferred panTo and it still leaks!
m_arrMaps["ShiftingMap"].panTo(objLatLong);
if (decLat == 20.5) {
//Leaks...
ResetTimer(39, -87);
}
else {
//Still leaks...
setTimeout("ShiftMap(20.5, -156);", 700);
}
}
function ResetTimer(decLat, decLng) {
m_Timer = setTimeout("ShiftMap(" + decLat + ", " + decLng + ");", 700);
}
</script>
</head>
<body onload="LoadMapAndStartShifting();">
<form id="form1" runat="server">
<div id="divMap"></div>
</form>
</body>
Why do you create a hashtable for the map? You could just create your map as a global variable instead. I'd be surprised that makes any difference, but your comment that "//Have tried both HashTable and Array (Both Leak)..." makes me wonder why you need either a hashtable or an array.
You could also get rid of the objMapOptions and objMapCenterPoint variables by just passing the structure of options directly into the Map() function, and likewise with the latLng into the panTo() functions.
<script type="text/javascript" language="javascript">
var m_Timer, map;
function LoadMapAndStartShifting() {
map = new google.maps.Map(document.getElementById("divMap"), { zoom: 9, center: new google.maps.LatLng(20.5, -156), mapTypeId: google.maps.MapTypeId.ROADMAP,
scaleControl: false, streetViewControl:false, zoomControl: false, mapTypeControl: false, panControl: false
});
setTimeout("ShiftMap(20.5, -156);", 700);
}
function ShiftMap(decLat, decLng) {
map.panTo(new google.maps.LatLng(decLat, decLng));
if (decLat == 20.5) {
//Leaks...
ResetTimer(39, -87);
}
else {
//Still leaks...
setTimeout("ShiftMap(20.5, -156);", 700);
}
}
function ResetTimer(decLat, decLng) {
m_Timer = setTimeout("ShiftMap(" + decLat + ", " + decLng + ");", 700);
}
</script>
Another suggestion, what about caching the maps? I don't know if this sample code is actually what you're doing, but if it's just flitting between two maps in this manner, couldn't you use the static map api, and cache the images and just use javascript to rotate the images?
Just had another thought. You're specifying v=2, i.e. getting the version 2 release of this API. The current version is 3.6. Suggest you try updating the version number (or just remove the v parameter to get the latest stable version) and see if that improves things. Possibly they've tidied up any garbage collection in more recent versions.
Looks like you're only storing one element in the "Hashtable" Object--["ShiftingMap"] -- it is not holding maps, it is holding the one map. You could just use a global variable to hold the map object.
The only repeating allocation is the new google.maps.LatLng(decLat, decLng)
which is created for every pan.
Suggestions:
1) The docs for LatLng don't give any warnings about needing to explicitly free the objects, so presumably the garbage collector should handle it.
2) Think about the overall problem--you're trying to continuously pan a Google map for 6 hours or more. Why do you think the leak is in your code? It's more likely that the problem is in G Maps. Probably the downloaded map image tiles are kept locally since it would be likely that the user would want to pan back.
As a test, try panning just back and forth between two points, rather than the current panning across the globe. (If I'm understanding your relative shifting correctly.)
See if the leak happens at the rate of memory / hour
3) Be sure to see if different browsers affect your problem differently. Esp try Google Chrome.
Added
I re-worked your demo code to move the LatLng call out of the loop and it still leaks. Also, it is best to use a non-string value in setTimeout
calls, so I changed that too. I also used the Maps API tutorial to set the doctype to what they recommend.
Result is below. It leaks on FF and Chrome. I filed a post at the official Google Maps API v3 forum.
It seems clear that the .panTo
method leaks. I guess your next step is to see if a Googler responds to the post.
Test case demo
Reworked test case:
<!DOCTYPE html>
<html>
<!-- http://sandbox.kluger.com/map_demo.html -->
<!-- See tutorial http://code.google.com/apis/maps/documentation/javascript/tutorial.html -->
<head>
<title>Map Shifter</title>
<style type="text/css">
#divMap { height: 400px; width:400px; position:absolute; top:10%;
left:10%; border: 2px solid #ff1100; }
</style>
<script type="text/javascript"
src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" language="javascript">
// Globals
var map,
mapCenterPoints,
currentPoint;
var ShiftMap = function() {
currentPoint = currentPoint == 0 ? 1 : 0; // update
map.panTo(mapCenterPoints[currentPoint]);
setTimeout(ShiftMap, 700);
}
function LoadMapAndStartShifting() {
mapCenterPoints = [new google.maps.LatLng(20.5, -156),
new google.maps.LatLng(39, -87)];
currentPoint = 0;
var objMapOptions = { zoom: 9,
center: mapCenterPoints[currentPoint],
mapTypeId: google.maps.MapTypeId.ROADMAP,
scaleControl: false, streetViewControl:false,
zoomControl: false, mapTypeControl: false,
panControl: false
}
map = new google.maps.Map(document.getElementById("divMap"),
objMapOptions);
setTimeout(ShiftMap, 700);
}
</script>
</head>
<body onload="LoadMapAndStartShifting();">
<div id="divMap"></div>
</body>
</html>
You can try refreshing the page once you have looped through your data using JavaScript. That might free the memory being used by the script running on the page:
How to redirect to another webpage in JavaScript/jQuery?
精彩评论