c# - 我的应用程序占用的内存随着时间的推移不断增加

标签 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,都必须在宿主表单的关闭事件中调用 LocalReport.ReleaseSandboxAppDomain()。否则,每次调用 Report 都会增加内存大小。

更新 2

在我的系统中找到了内存泄漏的真正原因。我正在使用我在流程布局面板中添加的复杂用户控件。

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

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

最佳答案

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

关于c# - 我的应用程序占用的内存随着时间的推移不断增加,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25823708/

相关文章:

c# - TabControl 取消选项卡的更改

c# - 在 C# winform 中删除列表框项目

c# - 有没有办法在 DBSet 的查询中包含本地缓存的项目?

javascript - 如何打开 HTML 链接并单击按钮?

c# - Visual Studio 在线 TF30063 : You are not authorized to access

php - 将两个 SELECT 连接到一个 mysql_query

mysql - 如何在不在 SQL 中创建重复行的情况下进行更新?

mysql - 为什么带有 InnoDB 的 MySQL 在键存在时进行表扫描并选择检查 70 倍以上的行?

c# - 创建插件应用程序的方法

c# - 在 GC 销毁尝试中保留对象