Устранение утечки памяти в IFeatureClass.Search (только в SDE с прямым подключением) ArcObjects?

16

Служба поддержки ESRI сообщает, что они воспроизвели проблему и открыли отчет об ошибке (NIM070156).

Я определил, что существует утечка памяти (в неуправляемой динамической памяти), которая возникает, когда инструмент в моей надстройке .NET / C # ArcMap выполняет пространственный запрос (возвращая объект ICursorfrom IFeatureClass.Searchс ISpatialFilterфильтром запроса). Все COM-объекты освобождаются, как только они больше не нужны (используя Marshal.FinalReleaseCOMObject).

Чтобы определить это, я сначала настроил сеанс PerfMon со счетчиками для личных байтов, виртуальных байтов и рабочего набора ArcMap.exe и отметил, что все три постоянно увеличиваются (примерно на 500 КБ за итерацию) с каждым использованием инструмента, выполняющего запрос , Важно отметить, что это происходит только при выполнении с классами пространственных объектов в SDE с использованием прямого соединения (хранилище ST_Geometry, клиент и сервер Oracle 11g). Счетчики оставались постоянными при использовании файловой базы геоданных, а также при подключении к более старому экземпляру SDE, в котором используется подключение к приложению.

Затем я использовал LeakDiag и LDGrapher (с некоторыми рекомендациями из этого поста в блоге ) и трижды регистрировал распределитель кучи Windows: при первой загрузке ArcMap и выборе инструмента для его инициализации, после запуска инструмента пару раз и после запуска это еще несколько десятков раз.

Вот результаты, показанные в представлении LDGrapher по умолчанию (общий размер): График LDGrapher, показывающий устойчивое увеличение использования памяти

Вот стек вызовов для красной линии: Стек вызовов, показывающий вызов sg.dll функции SgsShapeFindRelation2

Как вы можете видеть, SgsShapeFindRelation2функция sg.dll отвечает за утечку памяти.

Как я понимаю, sg.dll - это библиотека геометрии ядра, используемая ArcObjects, и SgsShapeFindRelation2предположительно там, где применяется пространственный фильтр.

Прежде чем делать что-то еще, я просто хотел посмотреть, сталкивался ли кто-нибудь еще с этой проблемой (или чем-то подобным) и что если они могли бы что-то сделать с этим. Кроме того, что может быть причиной этого только при прямом подключении? Похоже ли это на ошибку в ArcObjects, проблему конфигурации или проблему программирования?

Вот минимальная рабочая версия метода, который производит это поведение:

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

Вот мой обходной код, основанный на обсуждении ниже с 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
источник
1
+1 отличный анализ. Вы видите это только с прямым подключением ?
Кирк Куйкендалл
Только что протестировал его на старом сервере, который использует подключение к приложению, и там нет утечки памяти. Так что, конечно, кажется конкретным для прямого подключения
blah238
Какая версия ArcGIS (включая уровень пакета обновления)?
Филипп
Клиент: ArcGIS 10 SP2, Сервер: ArcGIS 9.3.1 SP1 (думаю, завтра перепроверю).
blah238
Есть ли какая-нибудь версия драйвера оракула, которую вы должны рассмотреть, это было давно, но, может быть, ODP.NET?
Кирк Куйкендалл

Ответы:

6

Это похоже на ошибку.

SG содержит библиотеки геометрии ArcSDE, а не библиотеки геометрии ArcObjects ... он используется в качестве предварительного фильтра перед тем, как тест попадет в библиотеки геометрии ArcObjects.

Попробуй это:

Опустить эту строку:

pSpatialFilter.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;

и поскольку вы не сохраняете ссылку на строку, вам не нужно не использовать курсоры повторного использования, поэтому установите флажок false в значение true.

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

Вы должны увидеть улучшение как в потреблении памяти, так и в скорости выполнения. Тем не менее, если ошибка все еще будет устранена, мы надеемся, что это значительно задержит ее :)

Раги Язер Бурхум
источник
1
Спасибо @Ragi - я попробовал обе модификации, но не было изменений в скорости утечки памяти.
blah238
Вы можете попробовать 2-х уровневое (прямое соединение) против 3-х уровневое (сервер приложений)? при условии, что у вас уже запущен сервер приложений, это должно быть только изменение в строке подключения sde.
Раги Язер Бурхум
о, только что увидел комментарий Кирка, так что это проблема прямого подключения. ИМХО, если бы вы делали это с 3-мя уровнями, есть вероятность, что вы бы увидели утечку на стороне сервера, но клиент не изменился. Могу ли я спросить, делаете ли вы что-нибудь с editsessions или клонированием геометрии?
Раги Язер Бурхум
1
Ну да и нет. В трехуровневом режиме giomgr остается резидентным, и при каждом подключении он запускает новый процесс gsrvr, который прекращается после вашего разъединения, поэтому, если утечка была, она исчезнет после вашего разъединения. Кроме того, мы не можем игнорировать тот факт, что прямое соединение имеет немного иной путь кода ... Попробуйте две вещи. Во-первых, просто полностью отключите пространственный фильтр и верните первый объект, а затем просто попробуйте esriSpatialRelEnvelopeIntersects. Я знаю, что семантически ни один из них не является тем же самым, но мы сначала хотим отследить утечку.
Раги Язер Бурхум
3
Да, поэтому оба метода избегают вызова sgShapeFindRelation2. Попробуйте это сейчас, esriSpatialRelEnvelopeIntersects на пространственном фильтре, чтобы sde сделал супер базовую предварительную фильтрацию, а затем ITopologicalOperator :: intersect для фактического тестирования на клиенте. Это может быть не так эффективно, как sgShapeFindRelation2, но это позволит избежать попадания в эту функцию и, следовательно, избежать утечки.
Раги Язер Бурхум
4

Если кто-то все еще заинтересован в этом, это было исправлено в версии 10.1.

Номер технической поддержки ESRI: NIM070156 и NIM062420

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

Трэвис
источник
В версии с исправленной версией ничего не указано, так что, думаю, мне просто поверить на это. Я не проверял на 10.1, хотя. Также проблема в моем отчете об ошибке не имеет ничего общего с маркировкой, поэтому я не уверен, почему они пометили ее как дубликат другой.
blah238
1

Вы можете попробовать следующий шаблон вместо try / finally { Marshal.FinalReleaseComObject(...) }:

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

Также работая с Direct Connect, я добился некоторого успеха в циклах, System.GC.Collect()периодически форсируя (каждые так много итераций), как бы неприятно это ни выглядело.

Дэвид Холмс
источник
Я применил подход ComReleaser, используя метод Джеймса Маккея для рециркуляции курсоров, описанный здесь: forums.arcgis.com/threads/… - это не имело никакого значения (в любом случае это просто обертка Marshal.ReleaseCOMObject, поэтому не слишком удивительно). Я также пытался использовать GC.Collect, но это также не имело никакого эффекта. Я должен был упомянуть, что я также посмотрел на управляемую память, используя несколько профилировщиков .NET, и ни один из них не обнаружил каких-либо управляемых объектов или накопления управляемой памяти, что заставило меня взглянуть на неуправляемую память.
blah238