单身中可能存在内存泄漏?

时间:2010-09-10 16:42:59

标签: c# .net memory-leaks producer-consumer

之前我没有问过这个问题。有人可以帮忙吗? 我在单例内部分析下面的代码,发现很多Rate对象(List<Rate>)都保留在内存中,虽然我清除它们。

protected void FetchingRates()
{
  int count = 0;

  while (true)
  {
    try
    {
      if (m_RatesQueue.Count > 0)
      {
        List<RateLog> temp = null;

        lock (m_RatesQueue)
        {
          temp = new List<RateLog>();
          temp.AddRange(m_RatesQueue);
          m_RatesQueue.Clear();
        }

        foreach (RateLog item in temp)
        {
          m_ConnectionDataAccess.InsertRateLog(item);
        }

        temp.Clear();
        temp = null;
      }
      count++;
      Thread.Sleep(int.Parse(ConfigurationManager.AppSettings["RatesIntreval"].ToString()));
    }
    catch (Exception ex)
    {  
      Logger.Log(ex);                 
    }
  }
} 

通过以下方式插入队列:

public void InsertLogRecord(RateLog msg)
{
  try
  {
    if (m_RatesQueue != null)
    {
      //lock (((ICollection)m_queue).SyncRoot)
      lock (m_RatesQueue)
      {
        //insert new job to the line and release the thread to continue working.
        m_RatesQueue.Add(msg);
      }
    }
  }
  catch (Exception ex)
  {
    Logger.Log(ex);  
  }
}

worker将速率日志插入DB,如下所示:

 internal int InsertRateLog(RateLog item)
    {
        try
        {
            SqlCommand dbc = GetStoredProcCommand("InsertRateMonitoring");
            if (dbc == null)
                return 0;
            dbc.Parameters.Add(new SqlParameter("@HostName", item.HostName));
            dbc.Parameters.Add(new SqlParameter("@RateType", item.RateType));
            dbc.Parameters.Add(new SqlParameter("@LastUpdated", item.LastUpdated));
            return ExecuteNonQuery(dbc);
        }
        catch (Exception ex)
        {
            Logger.Log(ex);
            return 0;
        }
    }

任何人都看到可能的内存泄漏?

4 个答案:

答案 0 :(得分:2)

  1. 我希望您正确处理ADO.NET对象。 (这只是一种很好的做法。)
  2. 任何迷路引用都会阻止GC收集您的RateLog个对象。
  3. 我建议您从创建RateLog对象的位置开始查看代码,并记下所有引用的位置。以下是一些需要考虑的事项。

    1. RateLog个对象是否订阅了任何活动?
    2. 您是否将RateLog个对象的集合保存在静态类的某个位置?
    3. 您还应该考虑在课堂上封装所有线程安全样板。

      public sealed class WorkQueue<T>
      {
          private readonly System.Collections.Generic.Queue<T> _queue = new System.Collections.Generic.Queue<T>();
          private readonly object _lock = new object();
      
          public void Put(T item)
          {
              lock (_lock)
              {
                  _queue.Enqueue(item);
              }
          }
      
      
          public bool TryGet(out T[] items)
          {
              if (_queue.Count > 0)
              {
                  lock (_lock)
                  {
                      if (_queue.Count > 0)
                      {
                          items = _queue.ToArray();
                          _queue.Clear();
                          return true;
                      }
                  }
              }
      
              items = null;
              return false;
          }
      }
      

      这将使您的代码更清晰:

      protected void FetchingRates()
      {
          int ratesInterval = int.Parse(ConfigurationManager.AppSettings["RatesIntreval"].ToString());
          int count = 0;
          var queue = new WorkQueue<RateLog>();
      
          while (true)
          {
              try
              {
                  var items = default(RateLog[]);
                  if (queue.TryGet(out items))
                  {
                      foreach (var item in items)
                      {
                          m_ConnectionDataAccess.InsertRateLog(item);
                      }
                  }
              }
              catch (Exception ex)
              {  
                  Logger.Log(ex);                 
              }
      
              Thread.Sleep(ratesInterval);
              count++;
          }
      } 
      

答案 1 :(得分:2)

看起来您没有丢弃挂在RateLog上的SqlCommand

答案 2 :(得分:0)

如何在循环外移动temp创建。您可能不允许GC清理。

protected void FetchingRates()
{
  int count = 0;
  List<RateLog> temp = new List<RateLog>();

  while (true)
  {
    try
    {
      if (m_RatesQueue.Count > 0)
      {    
        lock (m_RatesQueue)
        {
          temp.AddRange(m_RatesQueue);
          m_RatesQueue.Clear();
        }

        foreach (RateLog item in temp)
        {
          m_ConnectionDataAccess.InsertRateLog(item);
        }

        temp.Clear();
      }
      count++;
      Thread.Sleep(int.Parse(ConfigurationManager.AppSettings["RatesIntreval"].ToString()));
    }
    catch (Exception ex)
    {                   
    }
  }
} 

temp.Clear()之后,您可以尝试添加GC.Collect();。这不应该是永久的解决方案,但可以用于您的分析,以查看对象是否最终被清理。如果没有,那么某处可能仍然存在引用或事件。

答案 3 :(得分:0)

Clear()函数解构List。但是RateLog实例怎么样?他们的解构者被称为?锁定怎么样,这可能会阻止RateLog被删除。