我的应用程序占用的内存不断增加

时间:2014-09-13 13:29:05

标签: c# mysql winforms memory

我使用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,都必须在主机窗体的formclosing事件中调用LocalReport.ReleaseSandboxAppDomain()。否则每次调用Report都会增加内存大小。

更新2

在我的系统中找到内存泄漏的实际原因。我正在使用我在flowlayoutpanel中添加的复杂用户控件。

我使用普通的foreach循环来处理每个控件..但不知何故只有一半的对象被处理掉了。我将这个foreach循环嵌套在for循环中,计数器作为控件数。

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

4 个答案:

答案 0 :(得分:2)

SqlConnectionSqlCommandSqlDataReader都是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 - 也许是因为{{1}仅键入DataSource。你可以自己这样做:

object

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:0)

使用Timer而不是循环(将循环中的所有代码放入计时器刻度功能)。 或将所有代码放入计时器刻度线功能。