2014-09-13 177 views
1

我开发了一个使用C#,Winforms和Mysql的销售点系统。部署之后,我发现内存大小随着时间的推移而不断增加。在评估我的代码后,我觉得我的数据层可能是罪魁祸首。我使用类似这样的方法进行数据库通用调用我的应用程序占用的内存不断增加

public static DataTable ExecQuery(string query, List<SqlParam> sp_params, string db) 
{ 

     MySqlConnection sCon = new MySqlConnection(); 
     sCon.ConnectionString = "server=" + server + ";port=" + port + ";database=" + db + ";uid=" + user + ";pwd=" + password + ";charset=utf8;"; 


     MySqlCommand command = new MySqlCommand(); 
     command.CommandType = CommandType.Text; 
     command.Connection = sCon; 

     if (sp_params != null) 
     { 
      for (int i = 0; i < sp_params.Count; i++) 
      { 
       MySqlParameter sparam = new MySqlParameter(); 
       sparam.ParameterName = sp_params[i].Name; 
       sparam.MySqlDbType = sp_params[i].Type; 
       sparam.Value = sp_params[i].Value; 
       command.Parameters.Add(sparam); 
      } 
     } 
     command.CommandText = query; 
     sCon.Open(); 
     MySqlDataReader sd = command.ExecuteReader(); 



     DataTable dt = new DataTable(); 

     for (int fc = 0; fc < sd.FieldCount; fc++) 
     { 
      if (dt.Columns.Contains(sd.GetName(fc))) 
      { 
       dt.Columns.Add(sd.GetName(fc) + "1", sd.GetFieldType(fc)); 
      } 
      else 
      { 
       dt.Columns.Add(sd.GetName(fc), sd.GetFieldType(fc)); 
      } 
     } 

     while (sd.Read()) 
     { 
      DataRow dr = dt.NewRow(); 
      for (int fc = 0; fc < sd.FieldCount; fc++) 
      { 
       dr[fc] = sd.GetValue(fc); 
      } 
      dt.Rows.Add(dr); 
     } 

     sCon.Close(); 
     return dt; 

    } 

对于每个数据库调用,我们都使用这种方法。前端只需指定参数和查询。我怀疑这种静态方法会导致内存问题吗?

更新:

刚刚发现我的应用程序的另一个泄漏:

无论是的ReportViewer使用,LocalReport.ReleaseSandboxAppDomain()必须将主窗体的FormClosing事件被调用。否则,每次调用Report,都会增加内存大小。

更新2

找到内存泄漏的真正原因在我的系统。我正在使用一个复杂的用户控件,我将它添加到一个flowlayoutpanel中。

我正在使用正常的foreach循环来处理每个控件..但不知何故只有一半的对象被处置。我将这个foreach循环嵌套在for循环中,并将counter作为控件的数量。

 int count = flwControls.Controls.Count; 
     for (int i = 0; i < count; i++) 
     { 
      foreach (Control c in flwControls.Controls) 
      { 
       c.Dispose(); 
      } 
     } 
+0

内存泄漏.... – 2014-09-13 13:32:36

+0

@JoeDF是导致泄漏的方法的静态'性'?或代码 – 2014-09-13 13:39:08

+1

功能的任何特定部分看起来OK(除了缺少任何异常处理或使用/ finally语句来强制资源的释放)。是否有可能你分配和返回的表('DataTable dt = new DataTable();')永远不会被释放? – 2014-09-13 13:42:17

回答

2

SqlConnection,SqlCommandSqlDataReader都是IDisposable。前两个是另外密封的。假设您的MySqlXXX类正在封装这些类,则需要通过执行basic dispose pattern来使它们一次性丢弃,并通过将它们包装在using语句中进行处理。

DataTable也是一次性的,所以一定要在代码中使用较高的代码后处置它。

以下是如何处置这些资源的示例。 (请注意,我不能测试,因为我没有为MySqlXXX类的定义):

public static DataTable ExecQuery(string query, List<SqlParam> sp_params, string db) 
    { 
     using (MySqlConnection sCon = new MySqlConnection()) 
     using (MySqlCommand command = new MySqlCommand()) 
     { 
      sCon.ConnectionString = "server=" + server + ";port=" + port + ";database=" + db + ";uid=" + user + ";pwd=" + password + ";charset=utf8;"; 
      command.CommandType = CommandType.Text; 
      command.Connection = sCon; 

      if (sp_params != null) 
      { 
       for (int i = 0; i < sp_params.Count; i++) 
       { 
        MySqlParameter sparam = new MySqlParameter(); 
        sparam.ParameterName = sp_params[i].Name; 
        sparam.MySqlDbType = sp_params[i].Type; 
        sparam.Value = sp_params[i].Value; 
        command.Parameters.Add(sparam); 
       } 
      } 
      command.CommandText = query; 
      sCon.Open(); 
      using (MySqlDataReader sd = command.ExecuteReader()) 
      { 
       DataTable dt = new DataTable(); 
       for (int fc = 0; fc < sd.FieldCount; fc++) 
       { 
        if (dt.Columns.Contains(sd.GetName(fc))) 
        { 
         dt.Columns.Add(sd.GetName(fc) + "1", sd.GetFieldType(fc)); 
        } 
        else 
        { 
         dt.Columns.Add(sd.GetName(fc), sd.GetFieldType(fc)); 
        } 
       } 

       while (sd.Read()) 
       { 
        DataRow dr = dt.NewRow(); 
        for (int fc = 0; fc < sd.FieldCount; fc++) 
        { 
         dr[fc] = sd.GetValue(fc); 
        } 
        dt.Rows.Add(dr); 
       } 
       return dt; 
      } 
     } 
    } 
} 

更新

在回答下面的问题,我测试,发现废弃的DataGridView或更改DataSource不会自动处理以前的DataSource - 可能因为DataSource仅作为object键入。你可以自己这样做:

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
     dataGridView1.Disposed += dataGridView_Disposed; 
    } 

    static void dataGridView_Disposed(object sender, EventArgs e) 
    { 
     var dataGridView = sender as DataGridView; 
     if (dataGridView != null) 
     { 
      var oldTable = dataGridView.DataSource as IDisposable; 
      if (oldTable != null) 
       oldTable.Dispose(); 
     } 
    } 

    private void FillDataGridView(object sender, EventArgs e) 
    { 
     var oldTable = dataGridView1.DataSource as IDisposable; 
     DataTable table = GenerateTable(); 
     dataGridView1.DataSource = table; 
     if (oldTable != null) 
      oldTable.Dispose(); 
    } 
} 
+0

非常感谢您为一个恰当的和基于代码的解决方案。我关心这里是不会作为数据源中的数据表处理本身时像datagridview的家长控制设置 – 2014-09-13 14:51:00

+0

@Akshay Zadgaonkar - 你需要向我们展示您认为泄漏可能是代码。或者至少,[最小化,完整且可验证](http://stackoverflow.com/help/mcve)示例。 – dbc 2014-09-13 14:54:28

+0

@Akshay Zadgaonkar - 被说,也许这是相关的:http://stackoverflow.com/questions/16238206/where-is-my-memory-re-initializing-datatable – dbc 2014-09-13 14:59:11

1

您有内存泄漏。关闭scon后,您需要释放它。

1

代码在内存泄漏方面看起来很好。

应用程序的其他代码可能导致内存泄漏。 例如此代码返回的DataTable停留在内存上?

上述代码的一个建议是使用pooling=true以获得更好的性能。