委托和回调函数中的内存泄漏问题

4
 public delegate void SendCallbackType();

  public class SenderBase
  {
      SenderBase()
      {
         mySend = new SendCallbackType(SendData);
         mySend.BeginInvoke(SendCallback, null);
      }

    void SendData()
    {              
         // process / sending data
    }

    void SendCallback(IAsyncResult ar)
    {      
        **SendCallbackType worker = (SendCallbackType)((AsyncResult)ar).AsyncDelegate;
         worker.EndInvoke(ar);**

        //Above code is mandatory ? Working fine without them.

         mySend.BeginInvoke(SendCallback, null);

}

 // Test
  Dictionary<SenderBase> SenderCollection = new Dictionary();
  SenderCollection.Add(new SenderBase()); 
  SenderCollection.Remove(0);
 // Add and remove seven times

对象(SenderBase)不会被垃圾回收,它们将继续移动到下一代。

使用RedAnts内存分析工具,

enter image description here

有什么建议来清理这个对象。

谢谢。


1
参见类似问题:https://dev59.com/-3I-5IYBdhLWcg3wm5hG。只要委托和使用它的类共享相同的生命周期,就不会发生内存泄漏。 - Alois Kraus
如果没有内存泄漏,就不需要使用EndInvoke()吗? - C-va
你的SendCallback()看起来有点递归... - H H
1
根据经验数据,没有必要使用EndInvoke。 - Alois Kraus
1个回答

5
您一直在调用mySend.BeginInvoke()。因此,垃圾回收器始终在线程池线程的堆栈上看到对mySend对象的引用。因此,它不会对其进行回收。当然,代码将一直运行。
在这种情况下不调用EndInvoke()是一个坏主意,每个BeginInvoke()调用泄漏资源10分钟。默认的远程生存期,远程是实现委托BeginInvoke()方法的基础管道。
很难提出清理代码的建议,这只是没有多大意义。您可能会像启动while(true) {}循环的线程一样,这肯定更有效率。

感谢回复。我需要递归。我认为使用委托(使用线程池线程)如果我从字典中添加/删除实例,则没有开销。在应用程序生命周期中添加了近900个对象。至于EndInvoke(),我不想捕获异常(异常在senddata内部被捕获(未发布(通过事件引发))),并且当回调被调用时,senddata已经完成。还有其他原因需要使用EndInvoke吗? - C-va
线程池线程在我删除实例后自动死亡。从字典中删除对象后,我不再需要它。我尝试将委托设置为null,但结果仍然相同。 - C-va
1
你的注释和你的代码一样难以理解。我不认为我能帮助你。 - Hans Passant
请参考以下内容:https://dev59.com/JWbWa4cB1Zd3GeqPSxP- - C-va

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接