Lunlupus

QueryTask memory leak IE 8.0, Windows 7

Discussion created by Lunlupus on Sep 28, 2010
Hi Folks: I'm having trouble isolating an apparent memory leak in IE 8 using the javascript api.

What I have done is created a tool for selecting parcels from an existing map service published on our 9.3.1 ArcGIS Server. What I find is that the tools works as expected but our SQA guy noted that it grabs and keeps up to .5 GB of ram if he selects all the parcels on the map - as can be seen in Task Manager. (There are 14090 parcels in total)

I thought perhaps I wasn't releasing resources properly, or calling dojo.disconnect on the event handler handles, or had some circular reference or closure issues but I can't seem to find any - see attached code.

Then I went to the samples website and grabbed one of the QueryTask samples and started watching for the same thing - sure enough, this sample exhibits the same behavior but on a smaller scale - repeatedly clicking the map shows an increase in memory grabbed by IE which is never released until you close the browser.

http://resources.esri.com/help/9.3/arcgisserver/apis/javascript/arcgis/demos/querytask/query_gpresult.html

Now I know that selecting such an large number of parcels is not good practise performace wise - but, as all developers know, clients will do whatever they can to break your code. :)

Any suggestions would be helpful.

dojo.require("esri.toolbars.draw");
dojo.require("esri.tasks.query");
var tb = null;
var eventHandle = null;
var queryTask = null;
var query = null;
var symbol = null;

// stubbed out for brevity - I'll refer only to "Extent" selection
function SelectComplianceOrdersByPoint_onclick() { }

function SelectComplianceOrdersByPolygon_onclick() {}

function SelectComplianceOrdersByPolyline_onclick() {}

function SelectComplianceOrdersByExtent_onclick() {
    removeCursorClasses();
    $("#map_layers").addClass("RectangularCursor");
    ClearMapAndActivateSelect();
    FindComplianceOrdersInSelection("Extent");
}

function ClearMapAndActivateSelect() {
    map.graphics.clear();
    ClearHandles();
    navToolbar.deactivate();
    tb = new esri.toolbars.Draw(map);
}

// some handles are for methods in other modules
function ClearHandles() {
    if (graphicsOnMouseOverHandle != null) dojo.disconnect(graphicsOnMouseOverHandle);
    if (graphicsOnMouseOutHandle != null) dojo.disconnect(graphicsOnMouseOutHandle);
    if (graphicsOnClickHandle != null) dojo.disconnect(graphicsOnClickHandle);
    if (mapOnClickHandle != null) dojo.disconnect(mapOnClickHandle);
    mapOnClickHandle = null;
    graphicsOnClickHandle = null;
    graphicsOnMouseOutHandle = null;
    graphicsOnMouseOverHandle = null;
}

// stubbed out cases as well....
function FindComplianceOrdersInSelection(type) {
    switch (type) {
        case "Polygon": break;
        case "Extent":
            eventHandle = dojo.connect(tb, "onDrawEnd", findComplianceOrdersInExtent);
            tb.activate(esri.toolbars.Draw.EXTENT);
            break;
        case "Polyline": break;
        case "Point": break;
        default:
    }
}

function findComplianceOrdersInExtent(extent) {
    CreateQueryForSelection();
    ExecuteQueryForSelection(extent, "Extent");
    $("#map_layers").removeClass("RectangularCursor");
    tb.deactivate();
}

function findComplianceOrdersInPolygon(polygon) {}

function findComplianceOrdersOnPolyline(polyline) {}

function findComplianceOrdersByPoint(point) {}

function CreateQueryForSelection() {
    // disconnect from the onDrawEnd event since we have the results
    dojo.disconnect(eventHandle);
    queryTask = new esri.tasks.QueryTask(ServiceDirectory + CityOfSalisburyParcelsMapServer + "/1");
    query = new esri.tasks.Query();
    query.returnGeometry = true;
    query.outFields = QueryOutputFields;
    symbol = new esri.symbol.SimpleFillSymbol(esri.symbol.SimpleFillSymbol.STYLE_SOLID,
     new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_DASHDOT,
     new dojo.Color([255, 0, 0]), 2), new dojo.Color([255, 255, 0, 0.5]));
}

function ExecuteQueryForSelection(shape, type) {
    switch (type) {
        case "Polygon": break;
        case "Extent":
            query.geometry = new esri.geometry.Extent(shape);
            break;
        case "Polyline": break;
        case "Point": break;
        default:
    }
    shape = null;
    type = null;
    // this is where the memory gets grabbed
    queryTask.execute(query, ShowSelectionResults);
}

function ShowSelectionResults(featureset) {
    // memory never released after this point
    var parcels = "";
    query = null;
    queryTask = null;
    if (graphicsOnClickHandle != null) dojo.disconnect(graphicsOnClickHandle);
    if (tb != null) tb.deactivate();

    try {
    // the following builds a sql query to execute against the main DB for our application
        if (featureset.features.length <= 500) {
            for (var i = 0; i < featureset.features.length; i++) {
                if (featureset.features[i] == null) continue;
                if ((featureset.features[i].attributes.PARCEL_ID == null) || (featureset.features[i].attributes.PARCEL_ID == "")) continue;

                if (i != featureset.features.length - 1)
                    parcels += "'" + featureset.features[i].attributes.PARCEL_ID + "', ";
                else
                    parcels += "'" + featureset.features[i].attributes.PARCEL_ID + "'";
            }
            sql = "select Cog_ParcelIdName from Cog_codecompliance where Cog_ParcelIdName in (" + parcels + ")";
        }
        else
            sql = "select Cog_ParcelIdName from Cog_codecompliance";
    }
    catch (err) {
        alert(err.get_Message());
    }

    // this is a jQuery ajax call since I like it better than dojo
    // it calls a static c# method in the code behind of an empty
    // aspx page in my project
    $.ajax({
        type: "POST",
        async: "false",
        url: "methods.aspx/GetStringList",
        data: "{selectStatement:\"" + sql + "\"" + "}",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (msg) { // msg is a json object returned on success of the POST
            sql = "";
            if (msg.d != null) {
                for (var k = 0; k < featureset.features.length; k++) {
                    for (var j = 0; j < msg.d.length; j++) {
                        if (featureset.features[k].attributes.PARCEL_ID == msg.d[j]) {
                            var graphic = featureset.features[k];
                            graphic.setSymbol(symbol);
                            map.graphics.add(graphic);
                            break;
                        }
                    }
                }
                for (var n = 0; n < featureset.features.length; n++) featureset.features[n] = null;
                featureset = null;
                msg.d = null;
                msg = null;
                graphicsOnClickHandle = dojo.connect(map.graphics, "onClick", function (evt) {
                    
                    // prevent the event bubbling
                    dojo.stopEvent(evt);
                    LoadParcelInfoDialog(evt.graphic);
                });
            }
        }
    });
}

Outcomes