C# 请举例说明托管代码中的内存泄漏?
我在一次采访中被问到这个问题:众所周知,负责所有内存管理相关工作的垃圾收集器如何在C#as中出现内存泄漏问题?那怎么可能呢 来自:- 当在程序中分配内存并将其删除时,会发生内存泄漏 从未返回到操作系统,即使程序返回 不再使用内存。以下是内存泄漏的四种基本类型:C# 请举例说明托管代码中的内存泄漏?,c#,C#,我在一次采访中被问到这个问题:众所周知,负责所有内存管理相关工作的垃圾收集器如何在C#as中出现内存泄漏问题?那怎么可能呢 来自:- 当在程序中分配内存并将其删除时,会发生内存泄漏 从未返回到操作系统,即使程序返回 不再使用内存。以下是内存泄漏的四种基本类型: 在手动管理的内存环境中:内存由指针动态分配和引用。在释放内存之前,指针将被擦除。删除指针后,无法再访问内存,因此无法释放内存 在动态管理的内存环境中:内存被释放但从未收集,因为对对象的引用仍然处于活动状态。因为对对象的引用仍然处于活动状
- 在手动管理的内存环境中:内存由指针动态分配和引用。在释放内存之前,指针将被擦除。删除指针后,无法再访问内存,因此无法释放内存
- 在动态管理的内存环境中:内存被释放但从未收集,因为对对象的引用仍然处于活动状态。因为对对象的引用仍然处于活动状态,所以垃圾收集器从不收集该内存。这可能发生在系统或程序设置的参考中
- 在动态管理的内存环境中:垃圾收集器可以收集和释放内存,但不会将其返回到操作系统。当垃圾收集器无法将仍在使用的对象移动到内存的一部分并释放其余部分时,就会发生这种情况
- 在任何内存环境中:当许多大型对象被声明并且永远不允许离开作用域时,可能会导致内存管理不善。因此,内存被使用,并且永远不会被释放
这将解释一切。可能有多种原因,但这里有一个: 考虑两类:
class A
{
private B b;
}
class B
{
private A a;
}
如果您为这些类中的每一个创建一个对象,并交叉链接它们,然后这两个对象都超出范围,那么您仍然可以在另一个类中链接到它们中的每一个。GC很难捕捉到这些类型的交叉链接,并且可能会继续认为这两个对象仍在使用。当您实际上不再使用某个对象时,仅保留对该对象的引用是泄漏的好方法。尤其是当您将其存储在静态变量中时,这将创建一个在AppDomain生命周期内有效的引用,除非您显式地将其设置回null。如果这样一个引用存储在一个集合中,那么您可以得到一个真正的泄漏,该泄漏可能会使您的程序在OOM中崩溃。不平常 这样的漏洞可能很难找到。特别是事件可能很棘手,在订阅事件处理程序时,它们会让事件源对象添加对事件处理程序对象的引用。在C#代码中很难看到,因为在订阅事件时从未显式传递此消息。如果事件源对象存在很长时间,那么您可能会因为所有订阅服务器对象都保持被引用而遇到麻烦
棘手的问题确实需要内存分析器来诊断。示例将是一个类
子类包含ClickEventHandler
方法,该方法订阅了另一个类父类的事件ClickEvent
Child
类的GC
将被阻止,直到Parent
类超出范围。即使Child
超出范围,它也不会被GC收集,直到Parent
超出范围
在广播公司超出范围之前,GC不会收集订阅广播公司(事件
)的所有此类订户
所以,这是一种单向关系
Broadcaster(ClickEvent) -> Subscribers(ClickEventHandler)
所有ClickEventHandler
的GC将被阻止,直到ClickEvent
超出范围 垃圾收集器在循环引用方面没有问题。如果A或B的实例没有可访问的引用,则将收集它们。NET GC不计算引用,而是确定可访问性。我认为许多组件仍然必须显式关闭/释放,以释放它们的资源。
Broadcaster(ClickEvent) -> Subscribers(ClickEventHandler)