请举例说明托管代码中的内存泄漏?

时间:2013-10-27 13:56:42

标签: c#

我在接受采访时被问到这个问题:如何知道垃圾收集器负责所有与内存管理相关的工作,C#中是否会出现内存泄漏问题?那怎么可能呢?

5 个答案:

答案 0 :(得分:2)

来自MSDN: -

  

在程序中分配内存时发生内存泄漏   永远不会返回操作系统,即使程序确实如此   不再使用内存。以下是内存泄漏的四种基本类型:

     
      
  • 在手动管理的内存环境中:内存由指针动态分配和引用。在释放内存之前擦除指针。删除指针后,将无法再访问内存,因此无法释放。
  •   
  • 在动态管理的内存环境中:内存已被丢弃但从未收集过,因为对该对象的引用仍处于活动状态。由于对对象的引用仍处于活动状态,因此垃圾收集器永远不会收集该内存。使用由系统或程序设置的引用时可能会发生这种情况。
  •   
  • 在动态管理的内存环境中:垃圾收集器可以收集并释放内存,但永远不会将其返回给操作系统。当垃圾收集器无法将仍在使用的对象移动到内存的一部分并释放其余部分时,就会发生这种情况。
  •   
  • 在任何内存环境中:当声明许多大对象并且永远不允许离开作用域时,可能会导致内存管理不良。因此,使用内存并且永远不会释放。
  •   
 Dim DS As DataSet
  Dim cn As New SqlClient.SqlConnection("data source=localhost;initial catalog=Northwind;integrated security=SSPI")
  cn.Open()

  Dim da As New SqlClient.SqlDataAdapter("Select * from Employees", cn)
  Dim i As Integer
  DS = New DataSet()

  For i = 0 To 1000
      da.Fill(DS, "Table" + i.ToString)
  Next

虽然这段代码显然效率低且不实用,但它的目的是证明如果将对象添加到集合中(例如将表添加到DataSet集合中),只要集合保持活动状态,对象就会保持活动状态。如果在程序的全局级别声明了一个集合,并且在整个程序中声明了对象并将其添加到该集合中,这意味着即使对象不再在范围内,对象仍然处于活动状态,因为它们仍然被引用。

您也可以查看此参考: -

以上链接给出了一个非常好的结论

  

虽然.NET减少了你关注内存的需要,   你仍然必须注意你的应用程序对内存的使用   确保它表现良好和高效。 仅仅因为一个   应用程序管理并不意味着你可以抛出好的软件   工程实践窗外并依靠GC来执行   魔法。

答案 1 :(得分:2)

当你实际上对它没有任何用处时,只要坚持对某个对象的引用是一个很好的泄漏方法。特别是当您将其存储在 static 变量中时,它会创建一个存在于AppDomain生命周期的引用,除非您将其显式设置为null。如果这样的引用存储在一个集合中,那么你可以获得一个真正的泄漏,它可能会使你的程序崩溃。不常见。

这种泄漏很难找到。特别是事件可能很棘手,它们会在订阅事件处理程序时获取事件源对象以添加对事件处理程序对象的引用。很难在C#代码中看到,因为您在订阅活动时从未明确传递。如果事件源对象存在很长时间,那么您可能会遇到所有引用的订阅者对象的问题。

棘手的确实需要内存分析器进行诊断。

答案 2 :(得分:1)

示例将是一个包含Child方法的ClickEventHandler类,其订阅了另一个类ClickEvent的事件Parent

GC类超出范围之前,Child类的{p> Parent将被阻止。即使Child超出范围,GC也不会收集它直到Parent超出范围


在广播公司超出范围之前,所有订阅广播公司(Event)的订户都不会被GC收集。

所以,它是一种单向关系

Broadcaster(ClickEvent) -> Subscribers(ClickEventHandler)

所有ClickEventHandlers的GC都会被阻止,直到ClickEvent超出范围!

答案 3 :(得分:0)

例如:

您有主窗体和静态变量Popups [] collectionOfPopups的应用程序。 将所有应用程序弹出对象存储到静态数组中,永远不要删除它们。

因此,每个新弹出窗口都会占用内存,GC将永远不会释放它。

尝试阅读GC的工作原理 http://msdn.microsoft.com/en-us/library/ee787088.aspx

这将解释一切。

答案 4 :(得分:0)

可能有多种原因,但这里有一个:

考虑两个类:

class A
{
  private B b;
}

class B
{
  private A a;
}

如果为每个类创建一个对象,并将它们交叉链接,然后这两个对象都超出了范围,您仍然可以在另一个对象中链接到每个对象。 GC很难捕获这些交联,并且可能继续认为这两种物体仍在使用中。