C# C语言中的内存泄漏#

C# C语言中的内存泄漏#,c#,.net,memory-leaks,garbage-collection,managed,C#,.net,Memory Leaks,Garbage Collection,Managed,在托管系统中,当您确保所有句柄和实现IDispose的东西都被释放时,是否有可能泄漏内存 P>是否存在一些变量? < P>我不认为C++风格的内存泄漏是可能的。垃圾收集器应该考虑这些问题。即使对象不再使用,也可以创建聚集对象引用的静态对象。大概是这样的: public static class SomethingFactory { private static List<Something> listOfSomethings = new List<Something&

在托管系统中,当您确保所有句柄和实现
IDispose
的东西都被释放时,是否有可能泄漏内存


<> P>是否存在一些变量?

< P>我不认为C++风格的内存泄漏是可能的。垃圾收集器应该考虑这些问题。即使对象不再使用,也可以创建聚集对象引用的静态对象。大概是这样的:

public static class SomethingFactory
{
    private static List<Something> listOfSomethings = new List<Something>();

    public static Something CreateSomething()
    {
        var something = new Something();
        listOfSomethings.Add(something);
        return something;
    }
}
公共静态类SomethingFactory
{
私有静态列表listOfSomethings=新列表();
公共静态某物CreateSomething()
{
var something=新事物();
某物列表。添加(某物);
归还某物;
}
}

这显然是一个愚蠢的例子,但它相当于托管运行时内存泄漏。

一旦对象的所有引用都消失,垃圾收集器将在下一次传递时释放该对象。我不会说不可能泄漏内存,但这相当困难,为了泄漏内存,你必须在没有意识到的情况下引用一个对象


例如,如果您将对象实例化到一个列表中,然后在完成后忘记将其从列表中删除,并且忘记处理它们。

虽然框架中的某些内容可能存在泄漏,但更有可能的是,您的某些内容未被正确处理,或者某些内容阻止了GC对其进行处理,IIS将是这方面的主要候选人

请记住,并非.NET中的所有内容都是完全受管理的代码、COM互操作、文件io(如文件流、DB请求、图像等)


不久前,我们遇到了一个问题。IIS 6上的(.net 2.0)是,我们将创建一个映像,然后处理它,但IIS暂时不会释放内存。

如果未正确清理非托管资源,则可能会发生泄漏。实现的类可能会泄漏

但是,常规对象引用不需要像低级语言那样进行显式内存管理。

唯一的泄漏(运行时可能存在的bug除外,尽管不太可能是由于垃圾收集造成的)是针对本机资源的。如果您将P/Invoke转换到一个本机库中,该库打开文件句柄或套接字连接,或者代表您的托管应用程序打开任何内容,并且您从未显式关闭它们(并且没有在disposer或destructor/finalizer中处理它们),则可能会发生内存或资源泄漏,因为运行时无法为您自动管理所有这些内容


但是,如果您坚持使用纯托管资源,您应该会很好。如果您在没有调用本机代码的情况下遇到任何形式的内存泄漏,那么这就是一个bug。

如前所述,保留引用将导致内存使用量随时间增加。进入这种情况的一个简单方法是通过事件。如果您有一个长寿命对象,其中包含其他对象侦听的某个事件,如果侦听器从未被删除,那么长寿命对象上的事件将使这些其他实例在不再需要后保持长期活动。

是另一个潜在的泄漏源,例如内置对象反序列化器和高级SOAP/XML客户端。至少在早期版本的框架中,从属AppDomains中生成的代码从未卸载过…

委托可能会导致非直观的内存泄漏

无论何时从实例方法创建委托,对该实例的引用都存储在该委托中


此外,如果将多个委托合并为多播委托,只要在某个地方使用多播委托,就会有一大块对多个对象的引用被阻止被垃圾收集。

在.NET应用程序中发生内存泄漏的唯一原因是,尽管对象的生命周期已经结束,但它们仍然被引用。因此,垃圾收集器无法收集它们。它们变成了长寿命的物体


我发现,当对象生命结束时,订阅事件而不取消订阅,很容易导致泄漏。

事件处理程序是非常常见的非明显内存泄漏源。如果从object2订阅object1上的事件,然后执行object2.Dispose()并假装它不存在(并从代码中删除所有引用),则object1的事件中有一个隐式引用将防止object2被垃圾收集

MyType object2 = new MyType();

// ...
object1.SomeEvent += object2.myEventHandler;
// ...

// Should call this
// object1.SomeEvent -= object2.myEventHandler;

object2.Dispose();

这是一个常见的泄密案例-忘记轻松取消订阅活动。当然,如果object1被收集,object2也会被收集,但直到那时。

正如其他人所指出的,只要内存管理器中没有实际的错误,不使用非托管资源的类就不会泄漏内存

您在.NET中看到的不是内存泄漏,而是永远无法处理的对象。只要垃圾收集器能在对象图上找到对象,对象就不会被处理。因此,如果任何一个有生命的物体在任何地方都有对该物体的引用,它就不会被处理

事件注册是实现这一点的好方法。如果一个对象注册了一个事件,无论它注册的对象是什么,都有一个对它的引用,即使您消除了对该对象的所有其他引用,直到它注销(或者注册的对象变得不可访问),它将保持活动状态

因此,您必须注意在您不知情的情况下注册静态事件的对象。例如,
ToolStrip
的一个漂亮特性是,如果更改显示主题,它将自动在新主题中重新显示。它通过注册静态
SystemEvents.UserPreferenceChanged
事件来完成这个漂亮的特性。当您更改Windows主题时,会引发该事件,并且所有侦听该事件的
ToolStrip
对象都会得到通知,有一个新主题

好吧,那么假设你是y
private void DiscardMyToolstrip()
{
    Controls.Remove("MyToolStrip");
}
public class A
{
    B b;
    public A(B b) { this.b = b; }
    ~A()
    {
        b = new B();
    }
}

public class B
{
    A a;
    public B() { this.a = new A(this); }
    ~B()
    {
        a = new A(this);
    }
}

class Program
{
    static void Main(string[] args)
    {
        {
            B[] toBeLost = new B[100000000];
            foreach (var c in toBeLost)
            {
                toBeLost.ToString(); //to make JIT compiler run the instantiation above
            }
        }
        Console.ReadLine();
    }
}
var panel1 = new Panel();
var image = Image.FromFile("image/heavy.png");
for(var i = 0; i < 1000;++i){
  panel1.Controls.Add(new PictureBox(){Image = image});
}
panel1.Controls.Clear(); // => Memory Leak!
for (int i = panel1.Controls.Count-1; i >= 0; --i)
   panel1.Controls[i].Dispose();