2014-09-13 179 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以獲得更好的性能。