¿Se solucionó la pérdida de memoria en IFeatureClass.Search (solo en SDE con conexión directa) de ArcObjects?

16

El soporte de ESRI dice que ha reproducido el problema y ha abierto un informe de error (NIM070156).

He determinado que hay una pérdida de memoria (en la memoria de montón no administrada) que ocurre cuando una herramienta en mi complemento .NET / C # ArcMap realiza una consulta espacial (devolviendo un ICursordesde IFeatureClass.Searchcon un ISpatialFilterfiltro de consulta). Todos los objetos COM se lanzan tan pronto como ya no se necesitan (usando Marshal.FinalReleaseCOMObject).

Para determinar esto, primero configuré una sesión de PerfMon con contadores para Bytes privados, Bytes virtuales y Conjunto de trabajo de ArcMap.exe, y noté que los tres aumentaron constantemente (aproximadamente 500 KB por iteración) con cada uso de la herramienta que realiza la consulta . Crucialmente, esto solo ocurre cuando se realiza contra clases de entidad en SDE usando conexión directa (almacenamiento ST_Geometry, cliente y servidor Oracle 11g). Los contadores permanecieron constantes al usar una geodatabase de archivos, así como al conectarse a una instancia SDE anterior que usa la conexión de aplicaciones.

Luego utilicé LeakDiag y LDGrapher (con alguna guía de esta publicación de blog ) y registré el Windows Heap Allocator en tres ocasiones: cuando cargo ArcMap por primera vez y selecciono la herramienta para inicializarla, después de ejecutar la herramienta un par de docenas de veces, y después de ejecutarla unas pocas docenas más de veces.

Estos son los resultados que se muestran en la vista predeterminada de LDGrapher (tamaño total): Gráfico LDGrapher que muestra un aumento constante en el uso de memoria

Aquí está la pila de llamadas para la línea roja: Pila de llamadas que muestra la llamada sg.dll a la función SgsShapeFindRelation2

Como puede ver, la SgsShapeFindRelation2función en sg.dll parece ser la responsable de la pérdida de memoria.

Según tengo entendido, sg.dll es la biblioteca de geometría central utilizada por ArcObjects, y SgsShapeFindRelation2presumiblemente es donde se aplica el filtro espacial.

Antes de hacer cualquier otra cosa, solo quería ver si alguien más se había topado con este problema (o algo similar) y qué si podían hacer algo al respecto. Además, ¿cuál podría ser la razón de que esto ocurra solo con la conexión directa? ¿Suena esto como un error en ArcObjects, un problema de configuración o un problema de programación?

Aquí hay una versión mínima de trabajo del método que produce este comportamiento:

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

Aquí está mi código de solución basado en la discusión a continuación 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;
}
blah238
fuente
1
+1 gran análisis. ¿Lo estás viendo solo con una conexión directa ?
Kirk Kuykendall
Simplemente lo probé en un servidor anterior que usa la conexión de aplicaciones y no hay pérdida de memoria allí. ¡Tan seguro parece específico para la conexión directa!
blah238
¿Qué versión de ArcGIS (incluido el nivel de paquete de servicio)?
Philip
Cliente: ArcGIS 10 SP2, Servidor: ArcGIS 9.3.1 SP1 (creo que lo comprobaré mañana).
blah238
¿No hay alguna versión del controlador Oracle que deba considerar, ha pasado un tiempo, pero tal vez ODP.NET?
Kirk Kuykendall

Respuestas:

6

Esto parece un error.

SG contiene las bibliotecas de geometría de ArcSDE y no las bibliotecas de geometría de ArcObjects ... se usa como un prefiltro antes de que la prueba llegue a las bibliotecas de geometría de ArcObjects.

Prueba esto:

Omita esta línea:

pSpatialFilter.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;

y dado que no está guardando una referencia a la fila, no es necesario que no use cursores de reciclaje, por lo tanto, cambie el indicador falso a verdadero.

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

Debería ver una mejora tanto en el consumo de memoria como en la velocidad de ejecución. Sin embargo, si el error sigue siendo alcanzado, es de esperar que lo retrase dramáticamente :)

Ragi Yaser Burhum
fuente
1
Gracias @Ragi: probé ambas modificaciones pero no hubo cambios en la tasa de pérdida de memoria.
blah238
¿Puedes probar la conexión de 2 niveles (conexión directa) vs 3 niveles (servidor de aplicaciones)? siempre que tenga el servidor de aplicaciones en ejecución, esto solo debería ser un cambio en la cadena de conexión sde.
Ragi Yaser Burhum
oh, acabo de ver el comentario de Kirk, por lo que es un problema de conexión directa. En mi humilde opinión, si hiciste esto con 3 niveles, existe la posibilidad de que veas la fuga en el lado del servidor, pero el cliente permanecerá igual. ¿Puedo preguntarle si está haciendo algo con ediciones o geometrías de clonación?
Ragi Yaser Burhum
1
Pues sí y no. En el modo de 3 niveles, el giomgr permanece residente y para cada conexión genera un nuevo proceso gsrvr que morirá después de su desconexión, por lo que si la fuga estaba allí, desaparecería después de que se desconectó. Además, no podemos descartar el hecho de que la conexión directa tiene una ruta de código muy ligeramente diferente ... Pruebe dos cosas. Uno, simplemente apague el filtro espacial completamente y devuelva la primera característica y luego intente solo esriSpatialRelEnvelopeIntersects. Sé que semánticamente ninguno de estos es igual, pero primero queremos rastrear la fuga.
Ragi Yaser Burhum
3
Sí, entonces ambos métodos están evitando la llamada sgShapeFindRelation2. Pruebe esto ahora, esriSpatialRelEnvelopeIntersects en el filtro espacial para hacer que sde realice un prefiltrado súper básico, y luego ITopologicalOperator :: intersect para hacer la prueba real en el cliente. Esto puede no ser tan eficiente como sgShapeFindRelation2, pero evitará golpear esa función y, por lo tanto, evitará la fuga.
Ragi Yaser Burhum
4

Si alguien todavía está interesado en esto, se solucionó en la Versión 10.1.

Número de soporte técnico de ESRI: NIM070156 y NIM062420

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

travis
fuente
No incluye nada para la Versión Fija, así que supongo que tendré que aceptar tu palabra. Sin embargo, no he probado en 10.1. Además, el problema en mi informe de errores no tiene nada que ver con el etiquetado, así que no estoy seguro de por qué lo marcaron como un duplicado de ese otro.
blah238
1

Puede probar el siguiente patrón en lugar de try / finally { Marshal.FinalReleaseComObject(...) }:

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

También trabajando con Direct Connect, he tenido cierto éxito en los bucles al forzar System.GC.Collect()periódicamente (tantas iteraciones), por desagradable que parezca.

David Holmes
fuente
Le di una oportunidad al enfoque de ComReleaser usando el método de James MacKay para reciclar los cursores descritos aquí: forum.arcgis.com/threads/… - no hizo ninguna diferencia (simplemente envuelve a Marshal.ReleaseCOMObject de todos modos, así que no es demasiado sorprendente). También intenté usar GC.Collect pero tampoco tuvo ningún efecto. Debería haber mencionado que también miré la memoria administrada usando un par de perfiladores .NET y ninguno de ellos encontró ningún objeto administrado o memoria administrada que se acumulara, lo que me llevó a mirar la memoria no administrada.
blah238