Speicherleck in IFeatureClass.Search (nur bei SDE mit direkter Verbindung) von ArcObjects beheben?

16

Der ESRI-Support gibt an, das Problem reproduziert und einen Fehlerbericht geöffnet zu haben (NIM070156).

Ich habe festgestellt, dass ein Speicherverlust (im nicht verwalteten Heap-Speicher) vorliegt, der auftritt, wenn ein Tool in meinem .NET / C # ArcMap-Add-In eine räumliche Abfrage ausführt (und mit einem Abfragefilter ein ICursorfrom zurückgibt ). Alle COM-Objekte werden freigegeben, sobald sie nicht mehr benötigt werden (mit ).IFeatureClass.SearchISpatialFilterMarshal.FinalReleaseCOMObject

Um dies festzustellen, habe ich zuerst eine PerfMon-Sitzung mit Leistungsindikatoren für die privaten Bytes, virtuellen Bytes und den Arbeitssatz von ArcMap.exe eingerichtet und festgestellt, dass alle drei mit jeder Verwendung des Tools, das die Abfrage ausführt, stetig zunahmen (um ca. 500 KB pro Iteration) . Entscheidend ist, dass dies nur bei der Ausführung von Feature-Classes unter SDE unter Verwendung einer direkten Verbindung (ST_Geometry-Speicher, Oracle 11g-Client und -Server) auftritt. Die Leistungsindikatoren blieben konstant, wenn eine File-Geodatabase verwendet wurde sowie eine Verbindung zu einer älteren SDE-Instanz hergestellt wurde, die Application Connect verwendet.

Ich habe dann LeakDiag und LDGrapher (mit einigen Anleitungen aus diesem Blogbeitrag ) verwendet und den Windows-Heap-Allocator dreimal protokolliert: Beim ersten Laden von ArcMap und Auswählen des zu initialisierenden Tools, nachdem das Tool ein paar Dutzend Mal ausgeführt wurde und nachdem es ausgeführt wurde es noch ein paar Dutzend Mal.

Die folgenden Ergebnisse werden in der Standardansicht von LDGrapher angezeigt (Gesamtgröße): LDGrapher-Diagramm mit stetigem Anstieg der Speichernutzung

Hier ist die Aufrufliste für die rote Linie: Rufen Sie den Stapel auf, der den sg.dll-Aufruf der SgsShapeFindRelation2-Funktion zeigt

Wie Sie sehen, SgsShapeFindRelation2scheint die Funktion in sg.dll für den Speicherverlust verantwortlich zu sein.

Soweit ich weiß, ist sg.dll die von ArcObjects verwendete Geometrie-Kernbibliothek. SgsShapeFindRelation2Vermutlich wird dort der räumliche Filter angewendet.

Bevor ich etwas anderes mache, wollte ich nur sehen, ob jemand anderes auf dieses Problem gestoßen ist (oder etwas Ähnliches) und was, wenn er in der Lage ist, etwas dagegen zu unternehmen. Auch was könnte der Grund dafür sein, dass dies nur bei direkter Verbindung auftritt? Klingt dies nach einem Fehler in ArcObjects, einem Konfigurationsproblem oder einem Programmierproblem?

Hier ist eine minimale Arbeitsversion der Methode, die dieses Verhalten erzeugt:

private string GetValueAtPoint(IPoint pPoint, IFeatureClass pFeatureClass, string pFieldName)
{
    string results = "";
    ISpatialFilter pSpatialFilter = null;
    ICursor pCursor = null;
    IRow pRow = null;
    try
    {
        pSpatialFilter = new SpatialFilterClass();
        pSpatialFilter.Geometry = pPoint;
        pSpatialFilter.GeometryField = pFeatureClass.ShapeFieldName;
        pSpatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
        pSpatialFilter.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;
        pCursor = (ICursor)pFeatureClass.Search(pSpatialFilter, false);
        pRow = pCursor.NextRow();
        if (pRow != null)
            results = pRow.get_Value(pFeatureClass.FindField(pFieldName)).ToString();
    }
    finally
    {
        // Explicitly release COM objects
        if (pRow != null)
            Marshal.FinalReleaseComObject(pRow);
        if (pCursor != null)
            Marshal.FinalReleaseComObject(pCursor);
        if (pSpatialFilter != null)
            Marshal.FinalReleaseComObject(pSpatialFilter);
    }
    return results;
}

Hier ist mein Workaround-Code, der auf der folgenden Diskussion mit Ragi basiert:

private bool PointIntersectsFeature(IPoint pPoint, IFeature pFeature)
{
    bool returnVal = false;
    ITopologicalOperator pTopoOp = null;
    IGeometry pGeom = null;
    try
    {
        pTopoOp = ((IClone)pPoint).Clone() as ITopologicalOperator;
        if (pTopoOp != null)
        {
            pGeom = pTopoOp.Intersect(pFeature.Shape, esriGeometryDimension.esriGeometry0Dimension);
            if (pGeom != null && !(pGeom.IsEmpty))
                returnVal = true;
        }
    }
    finally
    {
    // Explicitly release COM objects
        if (pGeom != null)
            Marshal.FinalReleaseComObject(pGeom);
        if (pTopoOp != null)
            Marshal.FinalReleaseComObject(pTopoOp);
    }
    return returnVal;
}

private string GetValueAtPoint(IPoint pPoint, IFeatureClass pFeatureClass, string pFieldName)
{
    string results = "";
    ISpatialFilter pSpatialFilter = null;
    IFeatureCursor pFeatureCursor = null;
    IFeature pFeature = null;
    try
    {
        pSpatialFilter = new SpatialFilterClass();
        pSpatialFilter.Geometry = pPoint;
        pSpatialFilter.GeometryField = pFeatureClass.ShapeFieldName;
        pSpatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelEnvelopeIntersects;
        pFeatureCursor = pFeatureClass.Search(pSpatialFilter, true);
        pFeature = pFeatureCursor.NextFeature();
        while (pFeature != null)
        {
            if (PointIntersectsFeature(pPoint, pFeature))
            {
                results = pFeature.get_Value(pFeatureClass.FindField(pFieldName)).ToString();
                break;
            }
            pFeature = pFeatureCursor.NextFeature();
        }
    }
    finally
    {
        // Explicitly release COM objects
        if (pFeature != null)
            Marshal.FinalReleaseComObject(pFeature);
        if (pFeatureCursor != null)
            Marshal.FinalReleaseComObject(pFeatureCursor);
        if (pSpatialFilter != null)
            Marshal.FinalReleaseComObject(pSpatialFilter);
    }
    return results;
}
blah238
quelle
1
+1 gute Analyse. Sehen Sie es nur mit einer direkten Verbindung ?
Kirk Kuykendall
Ich habe es gerade auf einem älteren Server getestet, der Application Connect verwendet, und es gibt dort keinen Speicherverlust. Scheint also sicher spezifisch für direct connect zu sein!
blah238
Welche Version von ArcGIS (einschließlich Service Pack Level)?
Philip
Client: ArcGIS 10 SP2, Server: ArcGIS 9.3.1 SP1 (Ich denke, wir werden es morgen noch einmal überprüfen).
blah238
Gibt es nicht eine Orakel-Treiberversion, die Sie in Betracht ziehen müssen? Es ist schon eine Weile her, aber vielleicht ODP.NET?
Kirk Kuykendall

Antworten:

6

Das sieht aus wie ein Bug.

SG enthält die ArcSDE-Geometriebibliotheken und nicht die ArcObjects-Geometriebibliotheken. Es wird als Vorfilter verwendet, bevor der Test die ArcObjects-Geometriebibliotheken erreicht.

Versuche dies:

Lass diese Zeile weg:

pSpatialFilter.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;

und da Sie keinen Verweis auf die Zeile speichern, müssen Sie keine Recycling-Cursor verwenden. Setzen Sie daher das false-Flag auf true.

pCursor = (ICursor)pFeatureClass.Search(pSpatialFilter, true);

Sie sollten eine Verbesserung sowohl des Speicherverbrauchs als auch der Laufzeitgeschwindigkeit feststellen. Sollte der Fehler dennoch auftreten, wird er hoffentlich dramatisch verzögert :)

Ragi Yaser Burhum
quelle
1
Danke @Ragi - Ich habe beide Modifikationen ausprobiert, aber es gab keine Änderung der Speicherrate, die durchgesickert ist.
blah238
Können Sie versuchen, eine 2-Tier-Verbindung (Direktverbindung) mit einer 3-Tier-Verbindung (Anwendungsserver) zu verbinden? vorausgesetzt, der Anwendungsserver wird bereits ausgeführt, sollte dies nur eine Änderung der SDE-Verbindungszeichenfolge sein.
Ragi Yaser Burhum
Oh, habe gerade Kirks Kommentar gesehen, es handelt sich also um ein direktes Verbindungsproblem. IMHO, wenn Sie dies mit 3 Tier tun, gibt es die Möglichkeit, dass Sie das Leck auf der Serverseite sehen würden, aber der Client wird gleich bleiben. Darf ich fragen, ob Sie etwas mit Bearbeitungen oder Klonen von Geometrien tun?
Ragi Yaser Burhum
1
Ja und nein Im 3-Tier-Modus bleibt der giomgr resident und erzeugt für jede Verbindung einen neuen gsrvr-Prozess, der nach dem Trennen der Verbindung abbricht. Wenn also das Leck vorhanden wäre, würde es nach dem Trennen der Verbindung verschwinden. Wir können auch nicht die Tatsache außer Acht lassen, dass Direct Connect einen sehr geringfügig anderen Codepfad hat ... Probieren Sie zwei Dinge aus. Zum einen schalten Sie den räumlichen Filter einfach vollständig aus und geben die erste Funktion zurück. Dann versuchen Sie es einfach mit esriSpatialRelEnvelopeIntersects. Ich weiß, dass semantisch keines von diesen dasselbe ist, aber wir wollen zuerst das Leck verfolgen.
Ragi Yaser Burhum
3
Ja, also vermeiden beide Methoden den Aufruf sgShapeFindRelation2. Versuchen Sie dies jetzt, esriSpatialRelEnvelopeIntersects für den räumlichen Filter, damit sde eine super grundlegende Vorfilterung durchführt, und dann ITopologicalOperator :: intersect, um den eigentlichen Test auf dem Client durchzuführen. Dies ist möglicherweise nicht so effizient wie sgShapeFindRelation2, verhindert jedoch, dass diese Funktion beeinträchtigt wird, und verhindert daher das Leck.
Ragi Yaser Burhum
4

Wenn noch jemand daran interessiert ist, wurde es auf Version 10.1 behoben.

ESRI Technical Support Number: NIM070156 und NIM062420

http://support.esri.com/de/bugs/nimbus/TklNMDcwMTU2 http://support.esri.com/de/bugs/nimbus/TklNMDYyNDIw

Travis
quelle
Es wird nichts für Version Fixed aufgelistet, also muss ich mir wohl einfach dein Wort nehmen. Ich habe jedoch nicht bei 10.1 getestet. Auch das Problem in meinem Fehlerbericht hat nichts mit Beschriftung zu tun, also nicht sicher, warum sie es als ein Duplikat des anderen markiert haben.
blah238
1

Sie könnten stattdessen das folgende Muster ausprobieren try / finally { Marshal.FinalReleaseComObject(...) }:

using (ESRI.ArcGIS.ADF.ComReleaser cr) {
    var cursor = (ICursor) fc.Search(...);
    cr.ManageLifetime(cursor);
    // ...
}

Ich habe auch mit Direct Connect gearbeitet und habe einige Erfolge in Schleifen erzielt, indem ich in System.GC.Collect()regelmäßigen Abständen (bei so vielen Iterationen) Forcen durchgeführt habe, wie böse es auch aussieht.

David Holmes
quelle
Ich habe den ComReleaser-Ansatz mit der hier beschriebenen Methode von James MacKay zum Recyceln von Cursorn getestet : forums.arcgis.com/threads/… - es machte keinen Unterschied (es umschließt nur Marshal.ReleaseCOMObject, also nicht allzu überraschend). Ich habe auch versucht, GC.Collect zu verwenden, aber es hatte auch keine Wirkung. Ich hätte erwähnen sollen, dass ich mich mit einigen .NET-Profilern auch mit verwaltetem Speicher befasst habe und keiner von ihnen verwaltete Objekte oder verwalteten Speicher gefunden hat, was mich veranlasste, nicht verwalteten Speicher zu untersuchen.
blah238