解决ArcObjects的IFeatureClass.Search(仅在具有直接连接的SDE上)中的内存泄漏?


16

ESRI支持人员说,他们已重现了该问题,并已打开一个错误报告(NIM070156)。

我已确定,有内存泄漏(在非托管堆内存)时发生在工具我的.NET / C#ArcMap中附加执行空间查询(返回ICursorIFeatureClass.SearchISpatialFilter查询过滤器)。不再需要所有COM对象时,它们将被释放(使用Marshal.FinalReleaseCOMObject)。

为了确定这一点,我首先建立了一个带有ArcMap.exe的“专用字节”,“虚拟字节”和“工作集”计数器的PerfMon会话,并注意到,随着执行查询的工具的每次使用,这三者均稳步增加(每次迭代大约增加 500KB) 。至关重要的是,只有在使用直接连接(ST_Geometry存储,Oracle 11g客户端和服务器)对SDE上的要素类执行此操作时,才会发生这种情况。使用文件地理数据库以及连接到使用应用程序连接的较旧SDE实例时,计数器保持不变。

然后,我使用LeakDiagLDGrapher(在此博客文章中提供了一些指导)并三次登录Windows Heap Allocator:第一次加载ArcMap并选择工具对其进行初始化之后,运行了数十次该工具,然后运行了它几十次。

以下是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出色的分析。您是否仅通过直接连接看到它?
Kirk Kuykendall

刚刚在使用应用程序连接的较旧服务器上进行了测试,并且那里没有内存泄漏。因此,确定似乎特定于直接连接!
blah238

哪个版本的ArcGIS(包括Service Pack级别)?
菲利普(Philip)

客户端:ArcGIS 10 SP2,服务器:ArcGIS 9.3.1 SP1(我想明天会再次检查)。
blah238 2011年

您是否需要考虑一些Oracle驱动程序版本,已经有一段时间了,但也许是ODP.NET吗?
Kirk Kuykendall

Answers:


6

这看起来像个错误。

SG包含ArcSDE几何库,而不包含ArcObjects几何库...在测试达到ArcObjects几何库之前,它用作预过滤器。

尝试这个:

省略此行:

pSpatialFilter.SearchOrder = esriSearchOrder.esriSearchOrderSpatial;

并且由于您没有保存对行的引用,因此不需要使用回收游标,因此将false标志切换为true。

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

您应该在内存消耗和运行时速度上都得到改善。但是,如果仍然击中该错误,则可能会大大延迟它:)


1
感谢@Ragi-我尝试了两种修改,但内存泄漏率没有变化。
blah238 2011年

您可以尝试2层(直接连接)还是3层(应用程序服务器)连接吗?如果您已经在运行应用程序服务器,则这只能是sde连接字符串中的更改。
Ragi Yaser Burhum 2011年

哦,刚刚看到kirk的评论,所以这是直接联系的问题。恕我直言,如果您使用3层进行此操作,则可能会看到服务器端发生泄漏,但是客户端会保持不变。请问您是否正在使用editsessions或克隆几何进行任何操作?
Ragi Yaser Burhum 2011年

1
好吧,是的,不是的。在3层模式下,giomgr保持驻留状态,并且每次连接都会产生一个新的gsrvr进程,该进程将在断开连接后终止,因此,如果存在泄漏,断开连接后它将消失。另外,我们不能忽视直接连接确实具有略微不同的代码路径这一事实。尝试两件事。一个,只需完全关闭空间过滤器并返回第一个功能,然后再尝试esriSpatialRelEnvelopeIntersects。我知道从语义上讲,这些都不相同,但是我们要先跟踪泄漏。
Ragi Yaser Burhum 2011年

3
是的,因此这两种方法都避免调用sgShapeFindRelation2。现在尝试一下,在空间过滤器上使用esriSpatialRelEnvelopeIntersects使sde进行超基本的预过滤,然后使用ITopologicalOperator :: intersect在客户端上进行实际测试。这可能不如sgShapeFindRelation2高效,但是它将避免点击该函数,从而避免泄漏。
Ragi Yaser Burhum 2011年

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()周期性地强制执行循环(每多次迭代)都取得了一些成功,尽管看起来很讨厌。


我确实使用James MacKay的回收游标方法对ComReleaser方法进行了介绍,如下所述:forums.arcgis.com/threads/…-没什么 区别(无论如何,它只包装了Marshal.ReleaseCOMObject,所以并不令人惊讶)。我也尝试使用GC.Collect,但它也没有任何效果。我应该提到,我还使用几个.NET探查器查看了托管内存,但它们均未发现任何托管对象或托管内存堆积,导致我查看了非托管内存。
blah238 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.