[英]Memory leak OracleConnection with Oracle Data Access (ODP) in VB.Net, but not C#
[英]Creating a memory leak in C# or VB.Net
受到这个问题的启发,想知道在.Net 中创建 memory 泄漏的可能方法是什么。 我曾经发现一个带有 ODBC 数据访问的。 有没有人有最新版本的经验?
编辑:是否有任何看似正常、无害的用途会导致 memory 泄漏?
创建 memory 泄漏的最简单方法是滥用为互操作设计的设施,因为它们处理非托管 memory。
例如,分配一个指向GCHandle
的 GCHandle,并且永远不要释放它。
编辑:是否有任何看似正常、无害的用途会导致 memory 泄漏?
我只知道一个,尤其是在一些 UI 程序中。 这在 WinForms 中是可能的,但直到最近才因为 WPF 和 MVVM 而变得普遍:
许多人忽略的关键点是委托包含对其运行的 object 的引用。
因此,如果使用具有双向绑定(使用事件实现,由委托组成)的模式,例如 MVVM,并且如果视图更改为具有相同 ViewModel 的不同视图,则默认情况下,ViewModel 的更新仍然绑定到两者视图,这会导致旧视图泄漏。
理论上,任何委托都可能发生这种情况,但实际上并不常见,因为订阅者通常比发布者更长寿或取消订阅。
取消订阅 lambda 事件处理程序时也存在类似情况:
timer.Elapsed += (_, __) => myObj.Notify();
timer.Elapsed -= (_, __) => myObj.Notify();
在这种情况下,即使 lambda 表达式相同,它们代表不同的处理程序,因此Elapsed
事件仍将调用Notify
。 上面的第二行没有效果; 它不会取消订阅,也不会引发错误。
请注意,不正确的取消订阅通常会在测试期间被发现,因此它们很少会导致已发布代码中的“泄漏”。 相比之下,上述 MVVM 情况不会导致可观察到的副作用(除了 memory 和资源泄漏)。
GCHandle.Alloc()
是在 .NET 中创建“真正的”memory 泄漏的绝妙方法。 (“真正的”泄漏,因为
完全无法
到达,没有黑客/反射就无法到达,但仍然泄漏)
编辑
编辑:是否有任何看似正常、无害的用途会导致 memory 泄漏?
“看似正常”取决于一个人的知识/经验。
例如System.Windows.Forms.Timer
在启用时“扎根”自身(实际上是通过GCHandle.Alloc()
)。 如果您通过 Visual Studio 的图形编辑器将Timer
添加到Form
中,VS 将
Timer
添加到该“组件”集合的代码Dispose()
方法中处理“组件”集合中的所有内容这意味着事情将按预期工作,没有泄漏。
但是,如果您自己添加创建和启动Timer
的代码,很容易忘记添加停止/处理它的代码。 而且 Visual Studio 不会(不能)为你做这件事。
在这种情况下, Timer
将保持活动状态。 它永远不会被收集。 它将继续运行(并触发事件)。 即使Form
已关闭/处置。
而且,由于您通常会将Timer
的Tick
事件连接到Form
的某些成员 function ,因此Form
也将保持活动状态。 ( Timer
有根, Timer
引用事件委托,事件委托引用Form
。)
由于仍然有很多人不知道或不关心这样的东西,所以代码对他们来说看起来很“正常”。
如果您通读链接中提供的所有答案,它们几乎都适用于.Net。 虽然 CLR 和 JVM 是完全不同的系统,但它们的设计理念仍然非常相似(具体来说,它们都是托管系统),因此它们具有许多相同的优势和缺陷。
终结器滥用会导致“内存泄漏”,即这将创建一个 object,其 memory 永远不会被 GC 声明:
public class Foo
{
int[] value = new int[100];
~Foo()
{
GC.ReRegisterForFinalize(this);
}
}
要利用的一种模式是 class 有一个私有集合,没有人从中删除,但有人不断添加对象。
假设有一个后台线程应该处理来自 static 阻塞集合的元素并且线程死亡或阻塞。 然后集合只能增长,导致泄漏:
public class Test
{
static Test()
{
Task.Factory.StartNew(() =>
{
Random r = new Random();
try
{
while (true)
{
object o = col.Take();
//process o fails at some point
if (r.Next(100) == 0)
{
Console.WriteLine("Fail! No one is processing anymore.");
throw new Exception();
}
}
}
catch
{
Console.WriteLine("We caught the exception, but didn't resume processing");
}
});
}
private static BlockingCollection<object> col = new BlockingCollection<object>();
public void Add(object o)
{
col.Add(o);
}
}
class Program {
public static void Main(string[] args)
{
Test t = new Test();
while (true)
t.Add(new object());
}
}
for (;;)
Marshal.AllocHGlobal(0x400);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.