zoukankan      html  css  js  c++  java
  • DataTable.NewRow 内存无法释放问题

    作者:eaglet

    昨天做了一个自动生成Insert 语句的小工具,今天测试发现存在严重的内存无法释放问题,代码看了好几遍,没发现问题。后来用 .Net Memory Profiler 跟踪(跟踪方法见 用 .NET Memory Profiler 跟踪.net 应用内存使用情况--基本应用篇) 发现有数千个DataRow 没有释放,最后定位是DataTable.NewRow 的问题。

    先看一下有问题的代码

            public DataRow GetNextRow()
            {
                if (_DataReader.Read())
                {
                    DataRow row = _SchemaTable.NewRow();
     
                    foreach (DataColumn col in _SchemaTable.Columns)
                    {
                        row[col.ColumnName] = _DataReader[col.ColumnName];
                    }                
                    return row;
                }
                else
                {
                    return null;
                }
            }

    这段代码我希望通过SqlDataReader 对象 _DataReader 来获取一行数据。_SchemaTable 是一个DataTable 对象,这个DataTable对象没有记录,只存放Table 的Schema。乍一看好像没有什么问题,_SchemaTable.NewRow() 是根据_SchemaTable 的列信息生成一个新行,但这个新行并没有调用 _SchemaTable.Rows.Add 方法加入到_SchemaTable 表中,一般认为这个新生成的 DataRow 在使用完后会被自动回收,但实际情况并不是这样,只要_SchemaTable 不释放,_SchemaTable.NewRow 生成的所有DataRow都无法释放。

    网上搜了一下,有一位仁兄和我遇到同样问题 Table NewRow() Causes Memory Leak

    被采纳的解决意见是这样的:

    DataTable.NewRow() adds the created row to the DataTable's RecordManager. I am not entirely sure why this happens, but this is why it is not freed by the GC.

    It appears that there are only two ways to get rid of the DataRow:

    1. Add it to the table, then delete it.
    2. Call DataTable.Clear().

    也就是说DataTable.NewRow 方法创建的DataRow 对象会被加入到DataTable 的 RecordManager 中,我们可以通过以下两种方法来释放掉它:

    1. 通过 DataTable.Rows.Add 方法将这一行加入到DataTable 中,然后再通过 DataTable.Rows.Remove 方法删除它。

    2. 调用 DataTable.Clear() 方法释放。

    由于我这个应用中数据表只存放架构信息,始终是空表,所有我采用了第2种方法。加入 _SchemaTable.Clear() 这一句后内存泄漏问题解决。

    改正后的代码如下:

            public DataRow GetNextRow()
            {
                if (_DataReader.Read())
                {
                    DataRow row = _SchemaTable.NewRow();
     
                    foreach (DataColumn col in _SchemaTable.Columns)
                    {
                        row[col.ColumnName] = _DataReader[col.ColumnName];
                    }
     
                    _SchemaTable.Clear();
                    return row;
                }
                else
                {
                    return null;
                }
            }

    不好意思,上面代码还是有问题, _SchemaTable.Clear() 后DataRow 里面的数据都会被清空,必须在调用完新生成的DataRow后再 _SchemaTable.Clear 才行,特此更正。

    注:

    1. 本文原题用到内存泄漏,很多人认为这个词使用不当。是的,严格意义上讲这个地方的问题不是内存泄漏,现改为内存无法释放。

    2. 本文的问题是使用ado.net 的错误造成,错误比较隐蔽,我写出来提醒以后不要犯类似错误,这个问题并非.net 框架设计问题。

  • 相关阅读:
    Struts2的%,#,$的区别,UI标签及其表单radio,checkbox,select回显数据(七)
    Struts2的控制标签库和数据标签库(六)
    Struts2从后端向前端传递数据和OGNL访问用户自定义静态方法(五)
    两个小例子登录和显示全部用户信息的模块(四)
    Struts2的ServletAPI的获取和各种类型的数据获取(三)
    Action的三种实现方式,struts.xml配置的详细解释及其简单执行过程(二)
    Struts2的 两个蝴蝶飞,你好 (一)
    虚拟机安装Centos7系统后优化操作
    Java进程故障排查(CPU资源占用高,接口响应超时,功能接口停滞等)
    zabbix企业微信报警实现
  • 原文地址:https://www.cnblogs.com/eaglet/p/1582928.html
Copyright © 2011-2022 走看看