DataTable.NewRow 内存无法释放问题

本文介绍了一个关于ADO.NET中使用DataTable.NewRow方法导致的内存无法释放问题,并提供了解决方案。通过调用DataTable.Clear方法可以有效避免内存占用问题。
部署运行你感兴趣的模型镜像

作者: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
;
            }
        }
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

这段代码我希望通过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
;
            }
        }
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

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

注:

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

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

 

 

您可能感兴趣的与本文相关的镜像

Anything-LLM

Anything-LLM

AI应用

AnythingLLM是一个全栈应用程序,可以使用商用或开源的LLM/嵌入器/语义向量数据库模型,帮助用户在本地或云端搭建个性化的聊天机器人系统,且无需复杂设置

public static MemoryStream DataTableToExcel(DataTable dataTable) { // 创建一个Excel工作簿 IWorkbook workbook = new XSSFWorkbook(); ISheet sheet = workbook.CreateSheet("Sheet1"); // 创建表头 IRow headerRow = sheet.CreateRow(0); for (int i = 0; i < dataTable.Columns.Count; i++) { headerRow.CreateCell(i).SetCellValue(dataTable.Columns[i].ColumnName); } // 填充数据 for (int i = 0; i < dataTable.Rows.Count; i++) { IRow dataRow = sheet.CreateRow(i + 1); for (int j = 0; j < dataTable.Columns.Count; j++) { dataRow.CreateCell(j).SetCellValue(dataTable.Rows[i][j].ToString()); } } // 将Excel数据写入内存流 using (MemoryStream stream = new MemoryStream()) { workbook.Write(stream); return stream; } } [HttpPost(Name = "产品BOM表导出Excel文件")] public IActionResult Export_CpBom2([FromBody] Select select) { try { if (!Md5Helper.Md5Header(HttpContext.Request.Headers["SOAPAction"])) return Ok(new { code = 500, data = "身份验证失败" }); DataTable dt = db.Ado.GetDataTable($" Select d.id ,b.id as sid ,d.bom_type ,b.wl_id,b.wl_name,b.wl_monad,b.wl_type,c.company ,d.bom_number,d.bom_sm, d.is_bool " + $"FROM [XtQmDB].[dbo].[Gys_Wl] AS a " + $"Join [XtQmDB].[dbo].[Wl_Admin] AS b On a.wl_id = b.id " + $"Join [XtQmDB].[dbo].[Gys_Admin] AS c On a.gys_id = c.id " + $"Join [XtQmDB].[dbo].[Cp_Bom] AS d On a.id = d.gys_wl_id " + $"Where d.cp_id = 8 "); // 返回文件流 var fileStream = NpoiHelper.DataTableToExcel(dt); return File(fileStream, "application/vnd.ms-excel", $"{select.id}.xls"); } catch (Exception ex) { return Ok(new { code = 500, data = ex.Message }); ; } } 帮我看一下这个代码 是否有问题
10-01
private void btn_chart_Click(object sender, EventArgs e) { if (_dataTable.Rows.Count <= 0) { MessageBox.Show("无数据!", "Hint", MessageBoxButtons.OK); return; } chart1.Series.Clear(); chart1.Titles.Clear(); chart1.ChartAreas[0].AxisY.IsStartedFromZero = false; chart1.ChartAreas[0].AxisX.IntervalOffset = 50; chart1.ChartAreas[0].AxisX.Interval = 50; chart1.ChartAreas[0].AxisX2.MajorTickMark.LineWidth = 0; chart1.ChartAreas[0].AxisX2.MinorTickMark.LineWidth = 0; //chart1.ChartAreas[0].AxisX2.IntervalOffset = 50; //chart1.ChartAreas[0].AxisX2.Interval = 50; chart1.ChartAreas[0].AxisX.MajorGrid.Enabled = false; chart1.ChartAreas[0].AxisX.MinorGrid.Enabled = false; chart1.ChartAreas[0].AxisY.MajorGrid.Enabled = false; chart1.ChartAreas[0].AxisY.MinorGrid.Enabled = false; chart1.ChartAreas[0].AxisX2.MajorGrid.Enabled = false; chart1.ChartAreas[0].AxisX2.MinorGrid.Enabled = false; chart1.ChartAreas[0].AxisY2.MajorGrid.Enabled = false; chart1.ChartAreas[0].AxisY2.MinorGrid.Enabled = false; chart1.ChartAreas[0].AxisX.LabelStyle.Font = new Font("Microsoft Sans Serif", 9.5F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0))); chart1.ChartAreas[0].AxisX.LabelStyle.Angle = -45; chart1.ChartAreas[0].AxisX2.LabelStyle.Enabled = false; chart1.ChartAreas[0].AxisX2.LineWidth = 0; DataTable dttt = _dataTable.Copy(); var rateView = new DataView(dttt); DataTable maskscheduleTable = new DataTable(); maskscheduleTable.Columns.Add("stage", typeof(string)); maskscheduleTable.Columns.Add("mask", typeof(DateTime)); maskscheduleTable.Columns.Add("stage1", typeof(string)); maskscheduleTable.Columns.Add("mask1", typeof(DateTime)); //dttt.Columns.Add("mask", typeof(DateTime)); var rateView1 = new DataView(maskscheduleTable); DataSet ds = new DataSet(); // 将两个表添加到DataSet中(注意:表名不能重复,所以我们给两个表分别命名) maskscheduleTable.TableName = "ParentTable"; dttt.TableName = "ChildTable"; ds.Tables.Add(maskscheduleTable); ds.Tables.Add(dttt); if (cb_mask.Checked) { foreach (DataRow row in _dataTable.Rows) { if (row["maskschedule"] != DBNull.Value) { string maskactualStr = row["maskschedule"]?.ToString(); string stage = row["STEPSEQ"]?.ToString(); DateTime maskactualDt; maskactualDt = DateTime.Parse(maskactualStr); DataRow newRow = maskscheduleTable.NewRow(); newRow["mask"] = maskactualDt; newRow["stage"] = stage; maskscheduleTable.Rows.Add(newRow); } //else //{ // DataRow newRow = dttt.NewRow(); // newRow["mask"] = DBNull.Value; // dttt.Rows.Add(newRow); //} } if (!dttt.Columns.Contains("mask")) dttt.Columns.Add("mask", typeof(string)); //if (!dttt.Columns.Contains("mask1")) // dttt.Columns.Add("mask1", typeof(string)); DataColumn parentColumn = ds.Tables["ParentTable"].Columns["stage"]; DataColumn childColumn = ds.Tables["ChildTable"].Columns["stepseq"]; // 创建DataRelation,不创建约束(因为子表可能有父表没有的stage) DataRelation relation = new DataRelation("StageRelation", parentColumn, childColumn, false); ds.Relations.Add(relation); // 现在我们可以通过关系来获取父行 foreach (DataRow childRow in dttt.Rows) { DataRow[] parentRows = childRow.GetParentRows("StageRelation"); if (parentRows.Length > 0) { DataRow parentRow = parentRows[0]; // 取第一个匹配的父行 childRow["mask"] = parentRow["mask"]; //childRow["mask1"] = parentRow["mask1"]; } else { childRow["mask"] = DBNull.Value; //childRow["mask1"] = DBNull.Value; } } chart1.Series.Add("maskschedule"); chart1.Series["maskschedule"].ChartType = SeriesChartType.Point; chart1.Series["maskschedule"].BorderWidth = 3; chart1.Series["maskschedule"].YValueType = ChartValueType.DateTime; //chart1.Series["maskschedule"].Color = Clist[i * 2 + 1]; chart1.Series["maskschedule"].Points.DataBindXY(rateView, "stage", rateView, "mask"); chart1.Series["maskschedule"].MarkerSize = 5; //foreach (DataRow row in _dataTable.Rows) //{ // if (row["maskactual"] != DBNull.Value) // { // string maskactualStr1 = row["maskactual"]?.ToString(); // string stage1 = row["stepseq"]?.ToString(); // DateTime maskactualDt1; // maskactualDt1 = DateTime.Parse(maskactualStr1); // DataRow newRow = maskscheduleTable.NewRow(); // newRow["mask1"] = maskactualDt1; // newRow["stage1"] = stage1; // maskscheduleTable.Rows.Add(newRow); // } // //else // //{ // // DataRow newRow = dttt.NewRow(); // // newRow["mask"] = DBNull.Value; // // dttt.Rows.Add(newRow); // //} //} //chart1.Series.Add("maskactual"); //chart1.Series["maskactual"].ChartType = SeriesChartType.Spline; //chart1.Series["maskactual"].BorderWidth = 3; //chart1.Series["maskactual"].YValueType = ChartValueType.DateTime; //chart1.Series["maskactual"].XAxisType = AxisType.Secondary; ////chart1.Series["maskschedule"].Color = Clist[i * 2 + 1]; //chart1.Series["maskactual"].Points.DataBindXY(rateView1, "stage1", rateView1, "mask1"); //chart1.Series["maskactual"].MarkerSize = 5; //var filteredView = new DataView(filteredTable); //chart1.Series.Add("maskschedule"); //chart1.Series["maskschedule"].ChartType = SeriesChartType.Spline; //chart1.Series["maskschedule"].YValueType = ChartValueType.DateTime; //chart1.Series["maskschedule"].Points.DataBindXY(rateView1, "stepseq", rateView1, "maskschedule"); //chart1.Series["maskschedule"].MarkerSize = 5; //DataTable filteredTable1 = dttt.Clone(); //foreach (DataRow row in dttt.Rows) //{ // if (row["maskactual"] != DBNull.Value) // { // string maskactualStr = row["maskactual"]?.ToString(); // DateTime maskactualDt; // if (string.IsNullOrEmpty(maskactualStr) || !DateTime.TryParse(maskactualStr, out maskactualDt)) // { // row["maskactual"] = DBNull.Value; // 确保空值被正确处理 // } // else // { // row["maskactual"] = maskactualDt.ToOADate(); // } // if (row["maskactual"] != DBNull.Value) // { // filteredTable1.ImportRow(row); // } // } //} //var filteredView1 = new DataView(filteredTable1); //chart1.Series.Add("maskactual"); //chart1.Series["maskactual"].ChartType = SeriesChartType.Spline; //chart1.Series["maskactual"].YValueType = ChartValueType.DateTime; //chart1.Series["maskactual"].Points.DataBindXY(filteredView, "stage", filteredView, "maskschedule"); //chart1.Series["maskactual"].MarkerSize = 5; //foreach (DataPoint point in chart1.Series["maskschedule"].Points) //{ // string stage = point.AxisLabel; // DateTime maskactualDt = DateTime.FromOADate(point.YValues[0]); // Debug.WriteLine($"Chart point Stage:{stage}, maskschedule: {maskactualDt}"); //} } for (var i = 0; i < _lotTable.Rows.Count; i++) { var lotID = _lotTable.Rows[i]["LotID"].ToString().Replace(".", ""); double FQT = Convert.ToDouble(_lotTable.Rows[i]["FQT"].ToString()); var seriesName1 = FQT + "ForecastTime" + lotID; chart1.Series.Add(seriesName1); //由点改成线 //chart1.Series[seriesName1].ChartType = SeriesChartType.Point; chart1.Series[seriesName1].ChartType = SeriesChartType.Spline; chart1.Series[seriesName1].BorderWidth = 3; chart1.Series[seriesName1].YValueType = ChartValueType.DateTime; chart1.Series[seriesName1].Color = Clist[i * 2]; chart1.Series[seriesName1].Points.DataBindXY(rateView, "stage", rateView, seriesName1); //chart1.Series[seriesName1].MarkerSize = 5; //foreach (DataPoint point in chart1.Series["maskschedule"].Points) //{ // string stage = point.AxisLabel; // DateTime maskactualDt = DateTime.FromOADate(point.YValues[0]); // Debug.WriteLine($"Chart point Stage:{stage}, maskschedule: {maskactualDt}"); //} if (cb_trackintime.Checked) { var seriesName2 = FQT + "trackintime" + lotID; chart1.Series.Add(seriesName2); //chart1.Series[seriesName2].ChartType = SeriesChartType.Point; chart1.Series[seriesName2].ChartType = SeriesChartType.Spline; chart1.Series[seriesName2].BorderWidth = 3; chart1.Series[seriesName2].YValueType = ChartValueType.DateTime; chart1.Series[seriesName2].Color = Clist[i * 2 + 1]; chart1.Series[seriesName2].Points.DataBindXY(rateView, "stage", rateView, seriesName2); chart1.Series[seriesName2].MarkerSize = 5; } } var lotList = _lotTable.AsEnumerable().Select(t => t.Field<string>("LotID")).ToList(); string lotStr = String.Join("/", lotList); var title = new Title(); title.Font = new Font("Microsoft Sans Serif", 11.25F, FontStyle.Bold, GraphicsUnit.Point, ((byte)(0))); title.Text = lotStr + " Chart"; chart1.Titles.Add(title); }
最新发布
11-28
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值