为什么我的.net应用程序会占用内存?

时间:2012-04-30 09:05:04

标签: c# .net garbage-collection

我添加了一些.dispose()和.close()调用,它们似乎帮助了一小部分但我仍然看到应用程序在所有操作完成后使用600MB RAM。我错过了什么?

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

}

3 个答案:

答案 0 :(得分:3)

采取以下措施将感知的600 MB记录为测量的0字节:

  • 您的所有DisposeClose个来电都属于finally个版块,以防止异常安全;或者更好的是,了解评论已经建议的using声明。这种风格问题是许多致命内存泄漏的根源,但考虑到样本量有多小,可能不适合你。
  • 要测量分配和保留的内存,请在测量的块之前和之后执行GC.GetTotalMemory(true)。除非您理解并愿意支付性能成本,否则不要将其留在生产代码中。
  • 同时处理数据库连接。 (就客户端内存消耗而言,这是微不足道的,但不这样做可能会产生各种副作用。连接本身就是一种可耗尽的资源,它们也在数据库服务器上消耗内存,过时的连接可能会阻止某些数据库操作或混淆人类。 )
  • 在同一过程中运行整个测量周期两次,忽略第一组结果。 .NET框架可能会在干运行期间为自己的静态结构分配一些内存。如果内存泄漏不断增长或者可能无限大,那么内存泄漏只值得您关注。

答案 1 :(得分: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块中。

答案 2 :(得分:1)

它不进行垃圾收集 - 因为它不需要?

请注意,600mb在您的计算机上可能不是很多,并且GC仅在需要空间时启动(即其他应用程序要求空间)或达到阈值。

所以,除非这是一个问题,否则很可能是一个错误的观察。

运行内存探查器以查看实际内存负载是什么(它将强制收集,然后查看发生了什么,并允许您分析内存的使用情况和原因)。

不要(!)手动运行GC.Collect,除非在非常罕见的条件下(基本上:你因为rdoing而被解雇,现在争论为什么我不应该解雇你)。我需要记忆不是这样的条件 - GC.Collect有不好的副作用(混合代数统计)。