基于C#的ArcEngine二次开发37:循环查询过程的内存管理与性能优化(一)

简介: 基于C#的ArcEngine二次开发37:循环查询过程的内存管理与性能优化

注:文章第四部分搬运自ArcEngine空间查询优化;版权归原作者所有,特此声明

1 查询接口函数解析

1.1 IFeatureClass.Search()

1.1.1 函数说明

[C#]public IFeatureCursor Search ( IQueryFilterfilter, boolRecycling);

Search将返回一个满足IQueryFilter 设定的属性查询或空间查询条件的IFeatureCursor 对象;如果该参数为null,则返回要素类中的所有要素。参数recycling 控制行对象的分配行为,循环光标在提取单个要素目标都会重新进行初始化,并采用只读优化;在多次调用游标的NextFeature时,保持对要素的引用是非法的;通过回收游标返回的要素是不可修改的,不可回收游标在每个对象提取时,返回的是单独的对象,可以被多种行为的修改和存储。


The recycling parameter controls row object allocation behavior. Recycling cursors rehydrate a single feature object on each fetch and can be used to optimize read-only access, for example, when drawing. It is illegal to maintain a reference on a feature object returned by a recycling cursor across multiple calls to NextFeature on the cursor. Features returned by a recycling cursor should not be modified. Non-recycling cursors return a separate feature object on each fetch. The features returned by a non-recycling cursor may be modified and stored with polymorphic behavior.


The Geodatabase guarantees "unique instance semantics" on non-recycling feature objects fetched during an edit session. In other words, if the feature retrieved by a search cursor has already been instantiated and is being referenced by the calling application, then a reference to the existing feature object is returned.


Non-recycling feature cursors returned from the Search method *MUST* be used when copying features from the cursor into an insert cursor of another class.  This is because a recycling cursor reuses the same geometry and under some circumstances all of the features inserted into the insert cursor may have the same geometry.  Using a non-recycling cursor ensures that each geometry is unique.


1.1.2 依据点坐标查找图层中的第一个要素


The code in this document requires the following References added to the Visual Studio project:

ESRI.ArcGIS.Carto

ESRI.ArcGIS.Geodatabase

ESRI.ArcGIS.Geometry

ESRI.ArcGIS.System

///<summary>Finds the first feature in a GeoFeature layer by supplying an point.  The point could come from a mouse click in the map.</summary>
///
///<param name="searchTolerance">A System.Double that is the number of map units to search. Example: 25</param>
///<param name="point">An IPoint interface in map units where the user clicked on the map</param>
///<param name="geoFeatureLayer">An ILayer interface to search upon</param>
///<param name="activeView">An IActiveView interface</param>
/// 
///<returns>An IFeature interface that is the first feature found in the GeoFeatureLayer.</returns>
/// 
///<remarks></remarks>
public ESRI.ArcGIS.Geodatabase.IFeature GetFirstFeatureFromPointSearchInGeoFeatureLayer(
 System.Double searchTolerance,
 ESRI.ArcGIS.Geometry.IPoint point,
 ESRI.ArcGIS.Carto.IGeoFeatureLayer geoFeatureLayer,
 ESRI.ArcGIS.Carto.IActiveView activeView)
{
  if (searchTolerance < 0 || point == null || geoFeatureLayer == null || activeView == null)
  {
    return null;
  }
  ESRI.ArcGIS.Carto.IMap map = activeView.FocusMap; 
  // Expand the points envelope to give better search results    
  ESRI.ArcGIS.Geometry.IEnvelope envelope = point.Envelope;
  envelope.Expand(searchTolerance, searchTolerance, false);
  ESRI.ArcGIS.Geodatabase.IFeatureClass featureClass = geoFeatureLayer.FeatureClass;
  System.String shapeFieldName = featureClass.ShapeFieldName;
  // Create a new spatial filter and use the new envelope as the geometry    
  ESRI.ArcGIS.Geodatabase.ISpatialFilter spatialFilter = new ESRI.ArcGIS.Geodatabase.SpatialFilterClass();
  spatialFilter.Geometry = envelope;
  spatialFilter.SpatialRel = ESRI.ArcGIS.Geodatabase.esriSpatialRelEnum.esriSpatialRelEnvelopeIntersects;
  spatialFilter.set_OutputSpatialReference(shapeFieldName, map.SpatialReference);
  spatialFilter.GeometryField = shapeFieldName;
  // Do the search
  ESRI.ArcGIS.Geodatabase.IFeatureCursor featureCursor = featureClass.Search(spatialFilter, false);
  // Get the first feature
  ESRI.ArcGIS.Geodatabase.IFeature feature = featureCursor.NextFeature();
  if (!(feature == null))
  {
    return feature;
  }
  else
  {
      return null;
  }
}

1.2 IFeatureClass.Update()

1.2.1 函数说明

[C#]public IFeatureCursor Update ( IQueryFilterfilter, boolRecycling);


配合更新游标使用,通过过滤器filter进行属性或空间查询,如果选择一组特定的查询要素对其进行更新,则使用更新游标会比逐个要素更新更有效率,其更新是通过当前游标的位置执行。更新游标可作为带有多态行为(polymorphic behavior)要素子类的实例。更新游标即可在编辑会话一起使用,也可以单独使用;当在编辑会话内部使用更新游标时,这些变化知道编辑会话被保存才会提交到基础表中;网络要素类、拓扑要素类、参与组合关系或其他信息关系的要素类,只能通过编辑会话更新。如果你尝试在编辑会话之外使用更新游标,它将会失败;除此之外,编辑参与拓扑或几何网络的的要素必须要在编辑会话内。当在编辑会话中使用游标时,必须将其作用域设置为编辑操作;也就是说,游标可在编辑操作开始之后创建,且在编辑操作停止或中止之后不应再被使用。


Update opens an update cursor on the features specified by an attribute and/or spatial query as specified by the filter parameter. If a number of features selected by a particular query are to be updated and each feature is to be updated to a separate value then the update cursor is faster than doing an individual feature level update for each feature. The update is performed on the current 'cursor position'.Update cursors can be used on instances of Feature subclasses (such as network features), with guaranteed polymorphic behavior. Update cursors can be used either inside or outside of an edit session. If used inside an edit session, the changes are not committed to the base table until the edit session is saved. Network feature classes, Topology feature classes, feature classes that participate in composite relationships or other relationships with messaging may only be updated within an edit session. If you attempt to use an update cursor on one of these classes outside of an edit session, it will fail.  In addition, edits to features that participate in a Topology or Geometric Network must be bracketed within an edit operation.When using cursors within an edit session, they should always be scoped to edit operations. In other words, a cursor should be created after an edit operation has begun and should not be used once that edit operation has been stopped or aborted.

1.2.2 游标要素更新

public void UseUpdateCursor(IFeatureClass featureClass)
{
    // Restrict the number of features to be updated.
    IQueryFilter queryFilter = new QueryFilterClass();
    queryFilter.WhereClause = "NAME = 'Highway 104'";
    queryFilter.SubFields = "TYPE";
    // Use IFeatureClass.Update to populate IFeatureCursor.
    IFeatureCursor updateCursor = featureClass.Update(queryFilter, false);
    int typeFieldIndex = featureClass.FindField("TYPE");
    IFeature feature = null;
    try
    {
        while ((feature = updateCursor.NextFeature()) != null)
        {
            feature.set_Value(typeFieldIndex, "Toll Highway");
            updateCursor.UpdateFeature(feature);
        }
    }
    catch (COMException comExc)
    {
        // Handle any errors that might occur on NextFeature().
    }
    // If the cursor is no longer needed, release it.
    Marshal.ReleaseComObject(updateCursor);
}

1.3 IFeatureClass.Insert()

1.3.1 函数说明

[C#]public IFeatureCursor Insert (bool useBuffering);


插入要素

1.3.2 示例

示例1:使用插入游标插入要素


public static void InsertFeaturesUsingCursor(IFeatureClass featureClass, List <
    IGeometry > geometryList)
{
    using(ComReleaser comReleaser = new ComReleaser())
    {
        // Create a feature buffer.
        IFeatureBuffer featureBuffer = featureClass.CreateFeatureBuffer();
        comReleaser.ManageLifetime(featureBuffer);
        // Create an insert cursor.
        IFeatureCursor insertCursor = featureClass.Insert(true);
        comReleaser.ManageLifetime(insertCursor);
        // All of the features to be created are classified as Primary Highways.
        int typeFieldIndex = featureClass.FindField("TYPE");
        featureBuffer.set_Value(typeFieldIndex, "Primary Highway");
        foreach (IGeometry geometry in geometryList)
        {
            // Set the feature buffer's shape and insert it.
            featureBuffer.Shape = geometry;
            insertCursor.InsertFeature(featureBuffer);
        }
        // Flush the buffer to the geodatabase.
        insertCursor.Flush();
    }
}

示例2:使用仅加载模式插入要素

public static void LoadOnlyModeInsert(IFeatureClass featureClass, List < IGeometry >
    geometryList)
{
    // Cast the feature class to the IFeatureClassLoad interface.
    IFeatureClassLoad featureClassLoad = (IFeatureClassLoad)featureClass;
    // Acquire an exclusive schema lock for the class.
    ISchemaLock schemaLock = (ISchemaLock)featureClass;
    try
    {
        schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock);
        // Enable load-only mode on the feature class.
        featureClassLoad.LoadOnlyMode = true;
        using(ComReleaser comReleaser = new ComReleaser())
        {
            // Create the feature buffer.
            IFeatureBuffer featureBuffer = featureClass.CreateFeatureBuffer();
            comReleaser.ManageLifetime(featureBuffer);
            // Create an insert cursor.
            IFeatureCursor insertCursor = featureClass.Insert(true);
            comReleaser.ManageLifetime(insertCursor);
            // All of the features to be created are classified as Primary Highways.
            int typeFieldIndex = featureClass.FindField("TYPE");
            featureBuffer.set_Value(typeFieldIndex, "Primary Highway");
            foreach (IGeometry geometry in geometryList)
            {
                // Set the feature buffer's shape and insert it.
                featureBuffer.Shape = geometry;
                insertCursor.InsertFeature(featureBuffer);
            }
            // Flush the buffer to the geodatabase.
            insertCursor.Flush();
        }
    }
    catch (Exception)
    {
        // Handle the failure in a way appropriate to the application.
    }
    finally
    {
        // Disable load-only mode on the feature class.
        featureClassLoad.LoadOnlyMode = false;
        // Demote the exclusive schema lock to a shared lock.
        schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock);
    }
}

2 资源的释放

在进行数据查询时,如果不对游标进行释放,查询效率会越来越慢

2.1 使用Marsh.ReleaseComObject进行查询游标的释放

        private void find_allroads_lxbm(IFeature feature_ld, string lxbm)
        {
            try
            {
                ISpatialFilter spatialfilter = new SpatialFilterClass();
                spatialfilter.WhereClause = "[LXBM] = " + lxbm;
                spatialfilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
                //feature_ld该路段 feature_last下一路段
                while (feature_ld != null)
                {
                    IPolyline polyline = feature_ld.Shape as IPolyline;
                    //缓冲
                    IPoint end_pt = polyline.ToPoint;
                    ITopologicalOperator topo2 = end_pt as ITopologicalOperator;
                    IGeometry geo_buffer_end = topo2.Buffer(1);
                    spatialfilter.Geometry = geo_buffer_end;
                    IFeatureCursor featcursor_lastld = m_featcls.Search(spatialfilter, true);
                    IFeature feature_last = featcursor_lastld.NextFeature();
                    try
                    {
                        while (feature_last != null)
                        {                         
                           /****省略***/                                                 
                        }
                    }
                    catch (Exception exx)
                    {                        
                        MessageBox.Show(exx.ToString());
                    }                 
                    //下一个
                    feature_ld = feature_last;
             System.Runtime.InteropServices.Marshal.ReleaseComObject(featcursor_lastld);
                }
                System.Runtime.InteropServices.Marshal.ReleaseComObject(spatialfilter);
                垃圾回收
                System.GC.Collect();
                System.GC.WaitForPendingFinalizers();
            }
            catch (Exception exx)
            {
                MessageBox.Show(exx.ToString());
            }           
        }  

2.2 分析

情况一:在使用完游标之后,将其赋值为null;这时候对象实际上是没有被释放掉的,因此 可能会报 '超出打开游标最大数’;


情况二:在使用完游标时,在循环末尾,用Marshal.ReleaseComObject方法来释放com对象,同时在使用Marshal.ReleaseComObject 方法时,并没有增加多余的时间,循环执行时间上还是跟不释放对象一样(小数据量比较)。


结论:


在使用ArcEngine中的游标对象时,一定要在使用完之后进行对象的释放,否则会不定时出现上面的错误;而且需要使用marshal.releasecomobject方法来进行对象的释放,赋值为null是达不到目的的


Search()版本

IFeatureCursor pFeatureCursor = featureClass.Search(null, true);
IFeature pFeature = pFeatureCursor.NextFeature();
while(pFeature != null)
{
    //自已的一通操作
    pFeature.Store();//保存
    pFeature = pFeatureCursor.NextFeature();
}
Marshal.ReleaseComObject(pFeatureCursor);
pFeatureCursor = null;
GC.Collect();

Update()版本

IFeatureCursor pFeatureCursor = featureClass.Update(null, true);
IFeature pFeature = pFeatureCursor.NextFeature();
while(pFeature != null)
{
    //自已的一通操作
    pFeature.UpdateFeature(pFeature );//保存
    pFeature = pFeatureCursor.NextFeature();
}
Marshal.ReleaseComObject(pFeatureCursor);
pFeatureCursor = null;
GC.Collect();

Insert()版本

IFeatureCursor pFeatureCursor = featureClass.Insert(true);
IFeature pFeature = pFeatureCursor.NextFeature();
IFeatureBuffer pFeatureBuffer = featureClass.CreatFeatureBuffer();
while(pFeature != null)
{
    //对pFeatureBuffer执行一波操作
    pFeatureCursor.UpdateFeature(pFeatureBuffer);//保存
    pFeature = pFeatureCursor.NextFeature();
}
pFeatureCursor.Flush();
Marshal.ReleaseComObject(pFeatureCursor);
pFeatureCursor = null;
GC.Collect();

建议就是尽量避免对数据执行大量的循环查询操作,如果能采用内存优化的,就直接使用内存,这样可以快速提升程序的运行效率;但是对于,海量数据,迫不得已必须进行大量的循环操作时,请注意及时释放游标,释放内存资源,以确保程序性能。


相关文章
|
5月前
|
存储 Java C#
C# 中的值类型与引用类型:内存大小解析
C# 中的值类型与引用类型:内存大小解析
|
6天前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
|
23天前
|
SQL 缓存 分布式计算
C#如何处理上亿级数据的查询效率
C#如何处理上亿级数据的查询效率
13 1
|
2月前
|
存储 关系型数据库 MySQL
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
106 5
|
23天前
|
C# 开发工具 Windows
C# 获取Windows系统信息以及CPU、内存和磁盘使用情况
C# 获取Windows系统信息以及CPU、内存和磁盘使用情况
35 0
|
3月前
|
存储 监控 算法
Java内存管理:从垃圾收集到性能优化
【8月更文挑战第4天】在Java的世界中,内存管理是一块神秘的领域,它不仅关乎程序的稳定运行,更直接影响到系统的性能表现。本文将带你深入理解Java的垃圾收集机制,探讨如何通过合理的内存管理策略来提升应用效率。我们将一起分析JVM内存结构,探索不同的垃圾收集算法,并借助实际代码示例,学习如何监控和调优内存使用,以期达到减少延迟、防止内存泄漏的目的。
|
2月前
|
开发框架 自然语言处理 .NET
C#一分钟浅谈:LINQ 查询表达式的使用技巧
【9月更文挑战第6天】LINQ(Language Integrated Query)是C#开发中的强大工具,使查询数据集合变得简单且接近自然语言。本文从基础入手,通过具体示例讲解LINQ查询表达式的使用技巧,包括过滤、排序和分组等操作。同时,文章还探讨了常见问题及解决方法,如性能优化、过早枚举和类型转换等,帮助开发者写出更高效、易维护的代码。
85 15
|
3月前
|
Java PHP
从引用计数到循环垃圾回收——解锁PHP高效内存管理的秘密
【8月更文挑战第2天】深入理解PHP中的垃圾回收机制
78 3
|
3月前
|
存储 Java API
【Azure Developer】通过Azure提供的Azue Java JDK 查询虚拟机的CPU使用率和内存使用率
【Azure Developer】通过Azure提供的Azue Java JDK 查询虚拟机的CPU使用率和内存使用率
|
4月前
|
NoSQL Redis
Redis性能优化问题之禁用内存大页,如何解决
Redis性能优化问题之禁用内存大页,如何解决