Affrontare la perdita di memoria in IFeatureClass.Search (solo su SDE con connessione diretta) di ArcObjects?


16

Il supporto ESRI afferma di aver riprodotto il problema e di aver aperto una segnalazione di bug (NIM070156).

Ho determinato che esiste una perdita di memoria (nella memoria heap non gestita) che si verifica quando uno strumento nel mio componente aggiuntivo ArcMap .NET / C # esegue una query spaziale (restituendo un ICursorda IFeatureClass.Searchcon un ISpatialFilterfiltro di query). Tutti gli oggetti COM vengono rilasciati non appena non sono più necessari (utilizzando Marshal.FinalReleaseCOMObject).

Per determinare ciò, ho prima impostato una sessione PerfMon con contatori per i byte privati, i byte virtuali e il working set di ArcMap.exe e ho notato che tutti e tre sono aumentati costantemente (di circa 500 KB per iterazione) con ogni utilizzo dello strumento che esegue la query . Fondamentalmente, ciò si verifica solo se eseguito su classi di funzionalità su SDE utilizzando la connessione diretta (archiviazione ST_Geometry, client e server Oracle 11g). I contatori sono rimasti costanti durante l'utilizzo di un geodatabase di file, nonché durante la connessione a un'istanza SDE precedente che utilizza la connessione dell'applicazione.

Ho quindi usato LeakDiag e LDGrapher (con alcune indicazioni da questo post sul blog ) e ho registrato l'allocatore di heap di Windows tre volte: quando ho caricato ArcMap per la prima volta e ho selezionato lo strumento per inizializzarlo, dopo averlo eseguito una dozzina di volte e dopo averlo eseguito qualche altra dozzina di volte.

Ecco i risultati come mostrato nella vista predefinita di LDGrapher (dimensione totale): Grafico LDGrapher che mostra un costante aumento dell'utilizzo della memoria

Ecco lo stack di chiamate per la linea rossa: Stack di chiamate che mostra la chiamata sg.dll alla funzione SgsShapeFindRelation2

Come puoi vedere, la SgsShapeFindRelation2funzione in sg.dll sembra essere ciò che è responsabile della perdita di memoria.

A quanto ho capito sg.dll è la libreria di geometria di base utilizzata da ArcObjects ed SgsShapeFindRelation2è presumibilmente in cui viene applicato il filtro spaziale.

Prima di fare qualsiasi altra cosa, volevo solo vedere se qualcun altro si era imbattuto in questo problema (o qualcosa di simile) e cosa se qualcosa fosse stato in grado di fare al riguardo. Inoltre, quale potrebbe essere la ragione per cui ciò si verifica solo con la connessione diretta? Suona come un bug in ArcObjects, un problema di configurazione o un problema di programmazione?

Ecco una versione minima del metodo che produce questo comportamento:

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;
}

Ecco il mio codice di soluzione basato sulla discussione di seguito con Ragi:

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;
}

1
+1 ottima analisi. Lo vedi solo con una connessione diretta ?
Kirk Kuykendall,

L'ho appena testato su un server più vecchio che utilizza la connessione dell'applicazione e non vi è alcuna perdita di memoria lì. Quindi sicuramente sembra specifico per la connessione diretta!
blah238,

Quale versione di ArcGIS (incluso il livello di service pack)?
Filippo,

Client: ArcGIS 10 SP2, Server: ArcGIS 9.3.1 SP1 (penso, domani ricontrollerò).
blah238,

Non c'è una versione del driver Oracle che devi considerare, è passato del tempo, ma forse ODP.NET?
Kirk Kuykendall,

Risposte:


6

Sembra un bug.

SG contiene le librerie della geometria ArcSDE e non le librerie della geometria ArcObjects ... viene utilizzato come pre-filtro prima che il test raggiunga le librerie della geometria ArcObjects.

Prova questo:

Ometti questa riga:

pSpatialFilter.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;

e poiché non stai salvando un riferimento alla riga, non è necessario che tu non usi i cursori di riciclaggio, quindi imposta il falso flag su true.

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

Dovresti vedere un miglioramento sia nel consumo di memoria che nella velocità di runtime. Tuttavia, se il bug viene ancora colpito, si spera che possa ritardarlo drammaticamente :)


1
Grazie @Ragi - Ho provato entrambe le modifiche, ma non vi sono stati cambiamenti nella velocità di perdita della memoria.
blah238,

puoi provare la connessione a 2 livelli (connessione diretta) vs 3 livelli (server applicazioni)? a condizione che il server delle applicazioni sia già in esecuzione, ciò dovrebbe essere solo una modifica nella stringa di connessione sde.
Ragi Yaser Burhum,

oh, ho appena visto il commento di Kirk, quindi è un problema di connessione diretta. IMHO, se lo facessi con 3 livelli, c'è la possibilità che vedresti la perdita sul lato server, ma il client rimarrà lo stesso. Posso chiederti se stai facendo qualcosa con le modifiche o le geometrie di clonazione?
Ragi Yaser Burhum,

1
Bene, sì e no. Nella modalità a 3 livelli, giomgr rimane residente e per ogni connessione genera un nuovo processo gsrvr che morirà dopo la disconnessione, quindi se la perdita fosse presente, scomparirebbe dopo la disconnessione. Inoltre, non possiamo scartare il fatto che la connessione diretta abbia un percorso del codice leggermente diverso ... Prova due cose. Uno, basta spegnere completamente il filtro spaziale e restituire la prima funzione e quindi provare solo esriSpatialRelEnvelopeIntersects. So che semanticamente nessuno di questi è uguale, ma prima vogliamo tracciare la perdita.
Ragi Yaser Burhum,

3
Sì, quindi entrambi i metodi stanno evitando la chiamata sgShapeFindRelation2. Prova ora, esriSpatialRelEnvelopeIntersects sul filtro spaziale per fare in modo che sde esegua un pre-filtro super-base, quindi ITopologicalOperator :: intersect per eseguire il test effettivo sul client. Questo potrebbe non essere efficiente come sgShapeFindRelation2, ma eviterà di colpire quella funzione e quindi di evitare la perdita.
Ragi Yaser Burhum,

4

Se qualcuno è ancora interessato a questo, è stato risolto alla versione 10.1.

Numero supporto tecnico ESRI: NIM070156 e NIM062420

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


Non elenca nulla per la versione corretta, quindi suppongo che dovrò solo prendere la tua parola per questo. Non ho provato a 10.1. Anche il problema nella mia segnalazione di bug non ha nulla a che fare con l'etichettatura, quindi non so perché l'hanno contrassegnato come duplicato di quell'altro.
blah238,

1

È possibile provare il modello seguente anziché try / finally { Marshal.FinalReleaseComObject(...) }:

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

Lavorando anche con Direct Connect, ho avuto un certo successo nei loop forzando System.GC.Collect()periodicamente (ogni così tante iterazioni), per quanto brutto sembri.


Ho dato una possibilità all'approccio ComReleaser usando il metodo di James MacKay per riciclare i cursori qui descritto: forums.arcgis.com/threads/… - non ha fatto alcuna differenza (avvolge comunque Marshal.ReleaseCOMObject comunque non troppo sorprendente). Ho anche provato a utilizzare GC.Collect ma non ha avuto alcun effetto. Avrei dovuto menzionare che ho anche guardato alla memoria gestita usando un paio di profiler .NET e nessuno di loro ha trovato alcun oggetto gestito o accumulato memoria gestita, portandomi a guardare la memoria non gestita.
blah238,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.