Вирішення витоку пам'яті в IFeatureClass.Search (тільки на SDE при прямому підключенні) ArcObjects?


16

Підтримка ESRI каже, що вони відтворили проблему та відкрили звіт про помилку (NIM070156).

Я визначив , що є витік пам'яті (в некерованою динамічної пам'яті) , що відбувається , коли інструмент в моєму .NET / C # ArcMap надбудови виконує просторовий запит (який повертає ICursorз IFeatureClass.Searchз ISpatialFilterфільтром запиту). Усі об'єкти COM вивільняються, як тільки вони більше не потрібні (використовуються Marshal.FinalReleaseCOMObject).

Щоб визначити це, я спершу створив сеанс PerfMon з лічильниками для приватних байтів ArcMap.exe, віртуальних байтів та робочого набору, і зазначив, що всі три сили постійно збільшуються (приблизно в 500 КБ за ітерацію) з кожним використанням інструменту, який виконує запит. . Що головне, це відбувається лише тоді, коли виконується проти класів функцій на SDE за допомогою прямого підключення (ST_Geometry storage, Oracle 11g client and server). Лічильники залишалися постійними при використанні файлової бази даних, а також при підключенні до старого екземпляра SDE, який використовує підключення програми.

Потім я використав LeakDiag та LDGrapher (з деякими вказівками з цієї публікації в блозі ) і тричі реєстрував Windows Heap Allocator: коли я вперше завантажую 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;
}

Ось мій вирішувальний код на основі обговорення нижче з Рагі:

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 великий аналіз. Ви бачите це лише при прямому підключенні ?
Кірк Куйкендалл

Щойно тестували його на старшому сервері, який використовує підключення до програми, і там немає витоку пам'яті. Тож впевнено здається специфічним для прямого з'єднання!
blah238

Яка версія ArcGIS (включаючи рівень сервісного пакету)?
Філіп

Клієнт: ArcGIS 10 SP2, сервер: ArcGIS 9.3.1 SP1 (я думаю, завтра подвійно перевірять).
blah238

Чи не існує якоїсь версії драйвера Oracle, яку вам потрібно врахувати, пройшов час, але, можливо, ODP.NET?
Кірк Куйкендалл

Відповіді:


6

Це схоже на помилку.

SG містить бібліотеки геометрії ArcSDE, а не бібліотеки геометрії ArcObjects ... він використовується як попередній фільтр до того, як тест потрапить у бібліотеки геометрії ArcObjects.

Спробуйте це:

Пропустіть цей рядок:

pSpatialFilter.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;

і оскільки ви не зберігаєте посилання на рядок, вам не потрібно використовувати курсори для переробки, тому перемикайте помилковий прапор на істинний.

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

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


1
Дякую @Ragi - я спробував обидві модифікації, але не було зміни швидкості витоку пам'яті.
blah238

чи можете ви спробувати дворівневе (пряме підключення) проти 3-ярусне (сервер додатків) з'єднання? за умови, що у вас вже запущений сервер додатків, це має бути лише зміна рядка з'єднання sde.
Рагі Ясер Бурхум

о, щойно побачив коментар Кірка, тому це питання прямого підключення. ІМХО, якщо ви зробили це з 3-х рівнів, є ймовірність, що ви побачите витік на стороні сервера, але клієнт повинен залишатися таким же. Чи можу я запитати, чи ви щось робите з редакціями чи клонуванням геометрії?
Рагі Ясер Бурхум

1
Ну так і ні. У 3-х рівневому режимі giomgr залишається резидентом, і для кожного з'єднання він породжує новий процес gsrvr, який загине після відключення, тому якщо витік був там, він відключається після відключення. Крім того, ми не можемо оминути той факт, що пряме підключення має зовсім інший шлях коду ... Спробуйте дві речі. По-перше, просто вимкніть просторовий фільтр і поверніть першу функцію, а потім спробуйте просто esriSpatialRelEnvelopeIntersects. Я знаю, що семантично жодне з них не є однаковим, але ми хочемо спочатку відстежити витік.
Ragi Yaser Burhum

3
Так, обидва способи уникають виклику sgShapeFindRelation2. Спробуйте це зараз, esriSpatialRelEnvelopeIntersects на просторовому фільтрі, щоб змусити sde зробити надосновну попередню фільтрацію, а потім ITopologicalOperator :: перетинаються, щоб зробити фактичний тест на клієнті. Це може бути не настільки ефективно, як sgShapeFindRelation2, але це дозволить уникнути потрапляння на цю функцію і, отже, уникнути протікання.
Рагі Ясер Бурхум

4

Якщо хтось все ще цікавиться цим, це було виправлено у версії 10.1.

Номер технічної підтримки ESRI: NIM070156 та NIM062420

http://support.esri.com/uk/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/… - це нічого не змінило (він просто обертає маршала. ЗвільненняCOMObject так чи інакше не надто дивно). Я також спробував використовувати GC.Collect, але це також не мало ефекту. Я повинен був би згадати, що я також переглянув керовану пам'ять за допомогою декількох .NET-профілів, і жоден з них не знайшов керованих об'єктів або керованої пам'яті, що змусило мене подивитися на некеровану пам'ять.
blah238
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.