2

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、2 回目の実行では 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

4 に答える 4

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(); 
于 2013-04-26T14:06:58.890 に答える
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 は使い捨てなので、そうすることをお勧めします。

于 2013-04-26T14:03:56.660 に答える