我的记忆在哪里? 重新初始化DataTable

我有1个DataGridView,1个DataTable等应用程序。

我的“执行”方法(已编辑):

private void btnRunSQL_click() { string strConnStr = tbConnStr.Text; // connection string from textbox string strSQL = tbSql.Text; // query from textbox SqlDataAdapter dataAdapter = new SqlDataAdapter(strSQL, strConnStr); SqlCommandBuilder commandBuilder = new SqlCommandBuilder(dataAdapter); // clean memory // dtData DataTable is declared in main form class dtData = new DataTable(); dataAdapter.Fill(dtData); showMemoryUsage(); } 

这是我如何检查内存:

  public void showMemoryUsage() { Process proc = Process.GetCurrentProcess(); this.Text = "Peak memory: " + proc.PeakWorkingSet64 / 1024 / 1024 + "MB"; Application.DoEvents(); // force form refresh } 

当我多次运行此函数时 – 它使用越来越多的内存。 我正在处理非常大的数据集(1000000行),经过几次大的查询,我必须重新启动我的应用程序。

运行1M行查询后,我有内存使用大约900MB,第二次运行1100MB,1300MB等。

我认为重新初始化DataTable会释放我的记忆,但事实并非如此。 所以我重新初始化了BindingSource(与DataGridView连接),但它也没有帮助。 最后,我评论了我的BindingSource和DataGridView。

后来添加:

处置DataAdapter没有帮助。

我删除了DataGridView和绑定源。 没有帮助。

解决方案(我合并了几个答案并创建了测试应用程序而没有泄漏)

 using System; using System.Data; using System.Windows.Forms; using System.Data.SqlClient; using System.Diagnostics; // to run this code you need form and controls: // TextBox tbConnStr - textbox with SQL Server connection string // TextBox tbSQL - for SQL query to run/test // TextBox tbLog - for log display // Button btnRunSQL with OnClick event set to proper method // Button btnRunTest with OnClick event set to proper method namespace Test_datatable { public partial class Form1 : Form { DataTable dt; // i need this global public Form1() { InitializeComponent(); } private void btnRunSQL_Click(object sender, EventArgs e) { log("Method starts."); string strConnStr = tbConnStr.Text; string strSQL = tbSQL.Text; using (SqlDataAdapter da = new SqlDataAdapter(strSQL, strConnStr)) { using (SqlCommandBuilder cb = new SqlCommandBuilder(da)) { if (dt != null) { dt.Clear(); dt.Dispose(); log("DataTable cleared and disposed."); } dt = new DataTable(); da.Fill(dt); log("DataTable filled."); } } log("Method ends."); tbLog.Text += Environment.NewLine; } // prints time, string and memory usage on textbox private void log(string text) { tbLog.Text += DateTime.Now.ToString() + " " + text + memory() + Environment.NewLine; Application.DoEvents(); // force form refresh } // returns memory use as string, example: "Memory: 123MB" private string memory() { Process proc = Process.GetCurrentProcess(); return " Peak memory: " + (proc.PeakWorkingSet64 / 1024 / 1024).ToString() + "MB "; } // test method for 10 runs private void btnRunTest_Click(object sender, EventArgs e) { for (int i = 0; i < 10; i++) { btnRunSQL_Click(new object(), new EventArgs()); } } } } 

最好的猜测:通过Bindings仍然存在从GUI到旧DataTables的链接。 但实际的代码丢失了。

我在这方面的首选方法:

 if (bs != null) bs.Clear(); // most likely solution if (dtData != null) dtData.Clear(); // won't hurt dtData = new DataTable(); bs = new BindingSource(); 

对于初学者,你应该处置:

 private void btnRunSQL_click() { string strConnStr = tbConnStr.Text; // connection string from textbox string strSQL = tbSql.Text; // query from textbox using(SqlDataAdapter dataAdapter = new SqlDataAdapter(strSQL, strConnStr)) { using(SqlCommandBuilder commandBuilder = new SqlCommandBuilder(dataAdapter)) { // clean memory // dtData DataTable and BindingSource bs are declared in main form class dtData = new DataTable(); bs = new BindingSource(); dataAdapter.Fill(dtData); } } } 

但我相信至少部分问题包含在您未向我们展示的代码中。

编辑:请告诉我们您如何使用BindingSource。

EDIT2:您正在使用PeakWorkignSet64,您的应用程序是否以64位运行? 如果不是这个属性将不准确。 另外,尝试System.GC.GetTotalMemory(true)并告诉我们报告的内容。

SqlDataAdapter和SqlCommandBuilder是一次性的,你可以试试这样的东西

 private void btnRunSQL_click() { string strConnStr = tbConnStr.Text; // connection string from textbox string strSQL = tbSql.Text; // query from textbox using(var dataAdapter = new SqlDataAdapter(strSQL, strConnStr)) using(var commandBuilder = new SqlCommandBuilder(dataAdapter)) { dtData = new DataTable(); bs = new BindingSource(); dataAdapter.Fill(dtData); } } 

我认为这不会解决你的内存问题,但由于SqlDataAdapter和SqlCommandBuilder是一次性的,因此这是一个好主意。

为什么不重新填充旧的dtData

通过创建一个新的dtData实例,旧的将被垃圾收集器刷新一段时间。 您也可以手动调用GC,但如果有更好的解决方案,我不建议使用。

编辑:为了使其更清晰,请尝试使用此function:

 private void btnRunSQL_click() { string strConnStr = tbConnStr.Text; // connection string from textbox string strSQL = tbSql.Text; // query from textbox using(SqlDataAdapter dataAdapter = new SqlDataAdapter(strSQL, strConnStr)) { using(SqlCommandBuilder commandBuilder = new SqlCommandBuilder(dataAdapter)) { // edited after comment of Jon Senchyna dtData.Clear(); dataAdapter.Fill(dtData); } } }