ArcObjectsのIFeatureClass.Search(直接接続を使用するSDEのみ)のメモリリークに対処しますか?


16

ESRIサポートは、問題を再現し、バグレポート(NIM070156)を開いたと言います。

.NET / C#ArcMapアドインのツールが空間クエリ(クエリフィルターでICursorfrom IFeatureClass.Searchを返す)を実行するときに発生するメモリリーク(アンマネージヒープメモリ)があると判断しましたISpatialFilter。すべてのCOMオブジェクトは、必要がなくなるとすぐに解放されます(を使用Marshal.FinalReleaseCOMObject)。

これを判断するには、まず、ArcMap.exeのプライベートバイト、仮想バイト、ワーキングセットのカウンターを使用してPerfMonセッションを設定し、クエリを実行するツールを使用するたびに3つすべてが着実に増加(反復ごとに約500 KB)することに注意しました。重要なのは、直接接続(ST_Geometryストレージ、Oracle 11gクライアントおよびサーバー)を使用してSDEのフィーチャクラスに対して実行された場合のみです。ファイルジオデータベースを使用する場合、およびアプリケーション接続を使用する古いSDEインスタンスに接続する場合、カウンターは一定のままでした。

その後、LeakDiagLDGrapherを使用し(このブログ投稿からのいくつかのガイダンスを使用)、Windowsヒープアロケーターを3回ログに記録しました。さらに数十回。

LDGrapherのデフォルトビュー(合計サイズ)に表示される結果は次のとおりです。 メモリ使用量の着実な増加を示すLDGrapherグラフ

赤い線の呼び出し履歴は次のとおりです。 SgsShapeFindRelation2関数のsg.dll呼び出しを示す呼び出しスタック

ご覧のとおり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;
}

1
+1優れた分析。直接接続でのみ表示されますか?
カーククイケンドール

アプリケーション接続を使用する古いサーバーでテストしたところ、メモリリークはありません。確かに、直接接続に固有のようです!
blah238

ArcGISのバージョン(サービスパックレベルを含む)
フィリップ

クライアント:ArcGIS 10 SP2、サーバー:ArcGIS 9.3.1 SP1(明日、再確認します)。
-blah238

検討する必要のあるOr​​acleドライバのバージョンはありませんか?それはしばらく前ですが、おそらくODP.NETですか?
カーククイケンドール

回答:


6

これはバグのように見えます。

SGには、ArcObjectsジオメトリライブラリではなく、ArcSDEジオメトリライブラリが含まれています...テストがArcObjectsジオメトリライブラリにヒットする前のプレフィルターとして使用されます。

これを試して:

この行を省略します。

pSpatialFilter.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;

行への参照を保存していないので、リサイクルカーソルを使用する必要がないため、falseフラグをtrueに切り替えます。

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

メモリ消費と実行速度の両方が改善されるはずです。それでも、バグがまだヒットしている場合、これは劇的に遅延することを望みます:)


1
@Ragiに感謝-両方の変更を試みましたが、メモリリークのレートに変化はありませんでした。
blah238

2層(直接接続)対3層(アプリケーションサーバー)接続を試すことができますか?アプリケーションサーバーが既に実行されている場合、これはsde接続文字列の変更のみである必要があります。
ラギヤセルバーフム

ああ、kirkのコメントを見ただけなので、直接接続の問題です。私見、3層でこれを行った場合、サーバー側でリークが発生する可能性がありますが、クライアントは同じままです。編集セッションやジオメトリのクローン作成で何かしていることを聞いてもいいですか?
ラギヤセルバーフム

1
はい、そうです。3層モードでは、giomgrは常駐し、接続ごとに新しいgsrvrプロセスを生成します。これは、切断後に消滅するため、リークがあった場合、切断後に消滅します。また、ダイレクトコネクトのコードパスがわずかに異なるという事実を無視することはできません。2つのことを試してください。1つは、空間フィルターを完全にオフにして最初の機能を返し、esriSpatialRelEnvelopeIntersectsだけを試してください。意味的にこれらのどれも同じではないことは知っていますが、最初にリークを追跡したいと思います。
ラギヤーサーバーフム

3
そう、両方のメソッドはsgShapeFindRelation2の呼び出しを避けています。試してみてください。空間フィルターでesriSpatialRelEnvelopeIntersectを実行して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


Version Fixedには何もリストされていないので、私はあなたの言葉を受け入れなければならないと思います。ただし、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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.