【问题标题】:Memory leak in C# CodeC# 代码中的内存泄漏
【发布时间】:2018-08-28 23:10:35
【问题描述】:

我有一段代码每分钟都会访问数据库以检查是否有需要运行的报告,以及是否有运行它。

问题是我对数据库类的对象初始化会造成内存泄漏。如果我每次执行代码时都会查看任务管理器“用户对象”增长 5。

private void ReportRunTimer_Tick(object sender, EventArgs e)
        {
            DataConnection dataConnection = new DataConnection(); *<-- when executing this line User Object increasing.*
            try
            {
                reportsToRun = dataConnection.GetListOfTheReportForReportRunTick();
                if (reportsToRun.Count > 0)
                    foreach (string report in reportsToRun)
                    {
                        logs("Starting Automatic report generatin", "Successful");
                        Thread TicketReportMethodThread = new Thread(() => GenerateReport(report, 1));
                        TicketReportMethodThread.Start();
                    }
                dataConnection = null;
            } catch (Exception ex)
            {
                logs("Starting Automatic report generatin failed: " +ex.ToString(), "Error");
            }
            finally
            {
                reportsToRun.Clear();
            }
        }

数据连接类

public List<string> GetListOfTheReportForReportRunTick()
{
    List<string> RepoerList = new List<string>();
    connString = "Server=xxx;Port=xxx;Database=xxx;Uid=xxx;password=xxxx;SslMode=none";
    using (MySqlConnection mySqlConnection = new MySqlConnection(connString))
    {
        try
        {
            MySqlCommand mySqlCommand = mySqlConnection.CreateCommand();
            mySqlCommand.CommandText = "SELECT reportname FROM reports WHERE nextruntime < NOW()";
            mySqlConnection.Open();
            MySqlDataReader mySqlDataReader = mySqlCommand.ExecuteReader();

            while (mySqlDataReader.Read())
            {
                RepoerList.Add(mySqlDataReader["reportname"].ToString());
            }

        }
        catch (MySqlException ex)
        {
            hd.logs("Failed to get reports with reportstorun_tick Error: " + ex.ToString(), "Error");
            mySqlConnection.Close();
        }
        finally
        {
            mySqlConnection.Close();
            mySqlConnection.Dispose();
        }
    }
    return RepoerList;
}

DataConnection dataConnection = new DataConnection(); 用在了其他几个地方,这是唯一引起问题的地方。

如果我用公共列表 GetListOfTheReportForReportRunTick() 中的代码替换私有 void ReportRunTimer_Tick 中的代码,如下所示。问题不再存在,有什么想法吗?

private void ReportRunTimer_Tick(object sender, EventArgs e)
{
    List<string> reportsToRun = new List<string>();
    try
    {
        connString = "Server=xxx;Port=xxx;Database=xxx;Uid=xxx;password=xxxx;SslMode=none";
        using (MySqlConnection mySqlConnection = new MySqlConnection(connString))
        {
            try
            {
                MySqlCommand mySqlCommand = mySqlConnection.CreateCommand();
                mySqlCommand.CommandText = "SELECT reportname FROM reports WHERE nextruntime < NOW()";
                mySqlConnection.Open();
                MySqlDataReader mySqlDataReader = mySqlCommand.ExecuteReader();

                while (mySqlDataReader.Read())
                {
                    reportsToRun.Add(mySqlDataReader["reportname"].ToString());
                }



                if (reportsToRun.Count > 0)
                    foreach (string report in reportsToRun)
                    {
                        logs("Starting Automatic report generatin", "Successful");
                        Thread TicketReportMethodThread = new Thread(() => GenerateReport(report, 1));
                        TicketReportMethodThread.Start();

                    }
            }
            catch (MySqlException ex)
            {
                logs("Failed to get reports with reportstorun_tick Error: " + ex.ToString(), "Error");
                mySqlConnection.Close();
            }
            finally
            {
                mySqlConnection.Close();
                mySqlConnection.Dispose();
            }
        }

    }
    catch (Exception ex)
    {
        logs("Starting Automatic report generatin failed: " + ex.ToString(), "Error");
    }
    finally
    {
        reportsToRun.Clear();

    }

}

问题是由

引起的
DataConnection dataConnection = new DataConnection(); *<-- when executing this line User Object increasing.*
reportsToRun = dataConnection.GetListOfTheReportForReportRunTick();

但我不明白为什么。

【问题讨论】:

  • 你检查你的日志没有异常发生吗?无论如何,我认为使用using 是一个好主意,这样您就不必担心打开/关闭事情了。
  • 还意识到.NET 是一个垃圾收集系统。随着程序运行,内存使用量会增加,直到垃圾收集器启动。此时,您的部分(但不一定是全部)内存将被回收。根据您用于测量“泄漏”的内容,您可能看不到此回收的结果(尽管您明显的内存增长似乎会停止一段时间),如果您想强制垃圾收集作为测试的一部分, 使用GC.Collect();
  • 使用内存分析器。通过任务管理器观察内存使用情况非常具有误导性。
  • mySqlConnection.Close(); mySqlConnection.Dispose(); 删除那些 - 因为您使用的是using,它们没有做任何事情。
  • 我尝试用 public List GetListOfTheReportForReportRunTick() 中的代码替换私有 void ReportRunTimer_Tick(object sender, EventArgs e) 中的代码,并且“泄漏”停止了。 “用户对象”停止增加。我还注意到,当达到 10,000 时,应用程序总是崩溃。

标签: c# memory-leaks


【解决方案1】:

我认为内存泄漏是因为您没有处理 MySqlDataReader。您可以通过将其包装在这样的 using 语句中来做到这一点:

using(MySqlDataReader mySqlDataReader = mySqlCommand.ExecuteReader()){
    while (mySqlDataReader.Read())
    {
        RepoerList.Add(mySqlDataReader["reportname"].ToString());
    }
}

【讨论】:

  • 嗯,我认为 SqlCommand 也实现了 dispose,因此您可能还想尝试将它的用法包装在 using 语句中。否则,我在您发布的代码中看不到任何其他非托管资源。在这种情况下,您可能只想尝试使用 System.GC.Collect() 强制垃圾收集,看看是否能解决您的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-10
相关资源
最近更新 更多