1

I have added some .dispose() and .close() calls which seem to have helped a fraction but i'm still seeing the application using 600MB of RAM once all operations are complete. What am i missing?

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    public void CreateCSVFile(DataTable dt, string strFilePath)
    {
        #region Export Grid to CSV
        // Create the CSV file to which grid data will be exported.
        StreamWriter sw = new StreamWriter(strFilePath, false);
        // First we will write the headers.
        //DataTable dt = m_dsProducts.Tables[0];
        int iColCount = dt.Columns.Count;
        for (int i = 0; i < iColCount; i++)
        {
            sw.Write(dt.Columns[i]);
            if (i < iColCount - 1)
            {
                sw.Write(",");
            }
        }
        sw.Write(sw.NewLine);
        // Now write all the rows.
        int current_row = 0;
        foreach (DataRow dr in dt.Rows)
        {
            current_row++;
            if (current_row % 1000 == 0)
            {
                lbl_progress.Text = current_row.ToString() + " of " + dt.Rows.Count.ToString();
                this.Refresh();
                Application.DoEvents();
            }
            for (int i = 0; i < iColCount; i++)
            {
                if (!Convert.IsDBNull(dr[i]))
                {
                    if (dr[i] is string)
                    {
                        sw.Write("\"" + dr[i].ToString().Replace(Environment.NewLine,", ").Replace("\"", "") + "\"");
                    }
                    else
                    {
                        sw.Write(dr[i].ToString());
                    };
                }
                if (i < iColCount - 1)
                {
                    sw.Write("|");
                }
            }
            sw.Write(sw.NewLine);
        }
        dt.Dispose();
        sw.Close();
        #endregion
    }

    private void button1_Click(object sender, EventArgs e)
    {
        button1.Enabled = false;
        string strConn = ConfigurationManager.AppSettings["sql_connection"].ToString();
        SqlConnection conn = new SqlConnection(strConn);
        for (int i = 1; i <= int.Parse(ConfigurationManager.AppSettings["query_count"].ToString()); i++)
        {
            try
            {
                SqlDataAdapter da = new SqlDataAdapter(System.Configuration.ConfigurationManager.AppSettings["sql" + i.ToString()].ToString(), conn);
                DataSet ds = new DataSet();
                da.Fill(ds);
                da.Dispose();
                DataTable dt = ds.Tables[0];
                ds.Dispose();
                textBox1.Text += "\r\n[" + DateTime.Now.ToString("HH:mm:ss") + "] Starting sql" + i.ToString();
                this.Refresh();
                Application.DoEvents();
                CreateCSVFile(dt, System.Configuration.ConfigurationManager.AppSettings["output_path"].ToString() + i.ToString() + ".csv");
                textBox1.Text += "\r\n[" + DateTime.Now.ToString("HH:mm:ss") + "] Finished sql" + i.ToString();
                dt.Dispose();
                this.Refresh();
                Application.DoEvents();
            }
            catch (Exception ex)
            {
                textBox1.Text += "\n" + ex.Message;
                //break;
            }
        }
        textBox1.Text += "\r\n[" + DateTime.Now.ToString("HH:mm:ss") + "] All done";
        button1.Enabled = true;
    }

}
4

3 に答える 3

3

次のアクションを実行して、認識された 600 MB を測定値の 0 バイトに減らします。

  • すべてのDisposeand呼び出しはブロックにClose属し、例外セーフになります。finallyまたはさらに良いことusingに、コメントで既にアドバイスされているように、ステートメントについて学びます。このスタイルの問題は、多くの致命的なメモリ リークの根底にありますが、サンプルがどれほど小さいかを考えると、おそらくそうではありません。
  • 割り当てられたメモリと保持されたメモリを測定するGC.GetTotalMemory(true)には、測定されたブロックの前後に実行します。パフォーマンス コストを理解し、喜んで支払う場合を除き、これを製品コードに残さないでください。
  • データベース接続も破棄します。(これはクライアント側のメモリ消費に関しては些細なことですが、そうしないとさまざまな副作用が生じる可能性があります。接続はそれ自体が枯渇するリソースであり、データベース サーバーでもメモリを消費し、古い接続は一部のデータベース操作をブロックしたり、人間を混乱させたりする可能性があります。 )
  • 同じプロセスで測定サイクル全体を 2 回実行し、最初の一連の結果を無視します。.NET フレームワークは、予行演習中に独自の静的構造にメモリを割り当てる場合があります。メモリ リークが増加し続ける場合、またはサイズが無制限になる可能性がある場合にのみ、メモリ リークに注意する必要があります。
于 2012-04-30T09:35:47.053 に答える
3

最初に、Disposal はガベージ コレクションとは何の関係もないことを述べておきます。何かを破棄しても、GC の対象としてマークされたり、マネージ メモリが再利用されたりすることはありません。これは、コードが決定論的にリソース (通常は外部または管理されていない) を解放できるメカニズムです。

MSDNから引き出された:

ガベージ コレクションは、次の条件のいずれかに該当する場合に発生します。

  • システムの物理メモリが不足しています。
  • マネージ ヒープ上の割り当てられたオブジェクトによって使用されるメモリが、許容可能なしきい値を超えています。これは
    、マネージ ヒープで許容可能なメモリ使用量のしきい値を超えたことを意味します。この
    しきい値は、プロセスの実行中に継続的に調整されます。
  • GC.Collect メソッドが呼び出されます。ほとんどの場合、ガベージ コレクタは継続的に実行されるため、このメソッドを呼び出す必要はありません。この方法は、主に固有の状況とテストに使用されます。

これらは、GC が実行される条件です。

現在使用されている RAM と、アプリケーションが単に大きなワーキング セット (プロセスが OS によって与えられたメモリであり、必ずしも使用されるとは限らない) を持っていることとの間で混乱していると思います。ピーク時に 600Mb を使用した可能性があり、それ以上のスペースを要求するものは他にないため、プロセスに関連付けられた RAM はそのまま残されます。タスク マネージャーは、プロセスが実際に使用している RAM の量を適切に判断するのではなく、プロセスが使用するために割り当てられた RAM の量だけを判断します。

特定の時点で生きているオブジェクトの数を確認するには、適切なメモリ プロファイラを使用する必要があります。

また、実装するものはすべてステートメントIDisposable内で使用できます。using

using (var connection = new SqlConnection(""))
{
    connection.Open();
} // Dispose is called here, even on exception or return within using.

usingステートメントは内部try/finallyでブロックにコンパイルされるだけです。

于 2012-04-30T12:05:47.240 に答える
1

ガベージコレクションは行いません-必要ないので?

ほら、600mbはコンピュータ上ではそれほど多くないかもしれません、そしてGCはスペースが必要なとき(つまり他のアプリケーションがそれを要求するとき)またはしきい値に達したときにのみ起動します。

だから、それが問題でない限り、それは単純に間違った観察であるかもしれません。

メモリプロファイラーを実行して、実際のメモリ負荷が何であるかを確認します(コレクションを強制し、次に何が発生するかを確認し、メモリを使用するものとその理由を分析できます)。

非常にまれな状況でない限り、GC.Collectを手動で実行しないでください(基本的に:あなたはそうするために解雇されます、今私があなたを解雇すべきではない理由を議論してください)。私はメモリがそのような状態ではない必要があります-GC.Collectには悪い副作用があります(世代統計を混ぜ合わせます)。

于 2012-04-30T09:47:27.640 に答える