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

时间:2013-04-26 13:52:38

标签: c# memory

我有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());
            }
        }
    }
}

4 个答案:

答案 0 :(得分:3)

最佳猜测:通过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(); 

答案 1 :(得分:2)

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

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)并告诉我们报告的内容。

答案 2 :(得分:0)

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是一次性的,因此这是一个好主意。

答案 3 :(得分:-1)

为什么不重新填充旧的dtData

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

编辑: 为了更清楚,请尝试使用此功能:

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);
        }
     }
}