webentwicklung-frage-antwort-db.com.de

Was ist der richtige Weg, um Speicher in C # freizugeben

Ich habe einen Timer in C #, der Code innerhalb seiner Methode ausführt. Im Code verwende ich mehrere temporäre Objekte.

  1. Wenn ich so etwas wie Foo o = new Foo(); in der Methode habe, heißt das, dass ich jedes Mal, wenn der Timer tickt, ein neues Objekt und einen neuen Verweis auf dieses Objekt erstelle?

  2. Wenn ich string foo = null Habe und dann nur etwas Zeitliches in foo lege, ist es dasselbe wie oben?

  3. Wird das Objekt vom Garbage Collector jemals gelöscht und die Referenz oder die Objekte werden fortlaufend erstellt und verbleiben im Speicher?

  4. Wenn ich nur Foo o; Deklariere und es nicht auf irgendeine Instanz zeige, ist das dann nicht erledigt, wenn die Methode endet?

  5. Wenn ich sicherstellen möchte, dass alles gelöscht wird, wie gehe ich am besten vor:

    • mit der using-Anweisung innerhalb der Methode
    • durch Aufruf der dispose-Methode am Ende
    • wenn Sie Foo o; außerhalb der Methode des Timers platzieren und nur die Zuweisung o = new Foo() eingeben, wird der Zeiger auf das Objekt nach Beendigung der Methode gelöscht. Der Garbage Collector löscht das Objekt.
32
user579674

1.Wenn ich so etwas wie Foo o = new Foo () habe; Bedeutet das in der Methode, dass jedes Mal, wenn der Zeitgeber tickt, ein neues Objekt und eine neue Referenz auf dieses Objekt erstellt wird?

Ja.

2.Wenn ich den String foo = null habe und dann nur etwas Zeitliches in foo schreibe, ist es das gleiche wie oben?

Wenn Sie fragen, ob das Verhalten dasselbe ist, dann ja.

3. Löscht der Garbage Collector jemals das Objekt und die Referenz oder die Objekte werden fortlaufend erstellt und bleiben im Speicher?

Der von diesen Objekten verwendete Speicher wird mit Sicherheit gesammelt, nachdem die Verweise als nicht verwendet gelten.

4. Wenn ich nur Foo o erkläre; und nicht auf irgendeine Instanz zeigen, ist das nicht erledigt, wenn die Methode endet?

Nein, da kein Objekt erstellt wurde, gibt es kein Objekt zum Sammeln (Entsorgen ist nicht das richtige Wort).

5.Wenn ich sicherstellen möchte, dass alles gelöscht wird, wie gehe ich am besten vor?

Wenn die Klasse des Objekts IDisposable implementiert, sollten Sie Dispose so schnell wie möglich gierig aufrufen. Das Schlüsselwort using erleichtert dies, da es Dispose auf ausnahmesichere Weise automatisch aufruft.

Ansonsten müssen Sie nichts weiter tun, als die Verwendung des Objekts zu beenden. Wenn es sich bei der Referenz um eine lokale Variable handelt, kann sie beim Verlassen des Gültigkeitsbereichs abgerufen werden.1 Wenn es sich um eine Variable auf Klassenebene handelt, müssen Sie ihr möglicherweise null zuweisen, damit sie zulässig ist, bevor die enthaltende Klasse zulässig ist.


1Dies ist technisch falsch (oder zumindest ein wenig irreführend). Ein Objekt kann für die Sammlung in Frage kommen, lange bevor es seinen Gültigkeitsbereich verlässt. Die CLR wurde optimiert, um Speicher zu erfassen, wenn festgestellt wird, dass eine Referenz nicht mehr verwendet wird. In extremen Fällen kann die CLR ein Objekt sammeln, auch wenn eine ihrer Methoden noch ausgeführt wird!

pdate:

In diesem Beispiel wird gezeigt, dass der GC Objekte sammelt, obwohl diese möglicherweise noch im Gültigkeitsbereich liegen. Sie müssen ein Release-Build kompilieren und dieses außerhalb des Debuggers ausführen.

static void Main(string[] args)
{
    Console.WriteLine("Before allocation");
    var bo = new BigObject();
    Console.WriteLine("After allocation");
    bo.SomeMethod();
    Console.ReadLine();
    // The object is technically in-scope here which means it must still be rooted.
}

private class BigObject
{
    private byte[] LotsOfMemory = new byte[Int32.MaxValue / 4];

    public BigObject()
    {
        Console.WriteLine("BigObject()");
    }

    ~BigObject()
    {
        Console.WriteLine("~BigObject()");
    }

    public void SomeMethod()
    {
        Console.WriteLine("Begin SomeMethod");
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("End SomeMethod");
    }
}

Auf meinem Computer wird der Finalizer ausgeführt, während SomeMethod noch ausgeführt wird!

33
Brian Gideon

Der .NET-Garbage-Collector erledigt dies für Sie.

Es kann feststellen, wann Objekte nicht mehr referenziert werden und gibt (irgendwann) den ihnen zugewiesenen Speicher frei.

15
Yuck

Objekte können für die Garbage Collection verwendet werden, sobald sie vorhanden sind den Rahmen verlassen unerreichbar werden (danke ben!). Der Speicher wird erst freigegeben, wenn der Garbage Collector davon ausgeht, dass Ihnen der Speicher ausgeht.

Bei verwalteten Ressourcen weiß der Garbage Collector, wann dies der Fall ist, und Sie müssen nichts unternehmen.

Bei nicht verwalteten Ressourcen (z. B. Verbindungen zu Datenbanken oder geöffneten Dateien) kann der Garbage Collector nicht erkennen, wie viel Speicher sie belegen. Deshalb müssen Sie sie manuell freigeben (mithilfe von dispose oder noch besser mithilfe des using-Blocks).

Wenn Objekte nicht freigegeben werden, haben Sie entweder noch genügend Speicherplatz und es besteht keine Notwendigkeit, oder Sie verwalten einen Verweis auf sie in Ihrer Anwendung, sodass der Garbage Collector sie nicht freigibt (falls Sie diesen Verweis tatsächlich verwenden) gepflegt)

5
Martin Booth

Hier ist eine kurze Übersicht:

  • Sobald Referenzen weg sind, wird Ihr Objekt wahrscheinlich Müll gesammelt.
  • Sie können nur mit statistischen Erfassungen rechnen, die Ihre Heap-Größe normal halten, vorausgesetzt, alle Verweise auf Müll sind wirklich weg. Mit anderen Worten, es gibt keine Garantie dafür, dass ein bestimmtes Objekt jemals mit Müll entsorgt wird.
    • Daraus folgt, dass Ihr Finalizer auch niemals garantiert angerufen wird. Vermeiden Sie Finalizer.
  • Zwei häufige Ursachen für Undichtigkeiten:
    • Ereignishandler und Delegaten sind Verweise. Wenn Sie ein Ereignis eines Objekts abonnieren, verweisen Sie darauf. Wenn Sie einen Delegaten für die Methode eines Objekts haben, verweisen Sie darauf.
    • Nicht verwaltete Ressourcen werden per Definition nicht automatisch erfasst. Dafür ist das IDisposable-Muster gedacht.
  • Wenn Sie eine Referenz benötigen, die das Sammeln des Objekts nicht verhindert, schauen Sie in WeakReference nach.

Eine letzte Sache: Wenn Sie Foo foo; Deklarieren, ohne es zuzuweisen, müssen Sie sich keine Sorgen machen - nichts ist durchgesickert. Wenn Foo ein Referenztyp ist, wurde nichts erstellt. Wenn es sich bei Foo um einen Wertetyp handelt, wird er dem Stapel zugewiesen und somit automatisch bereinigt.

2
Kevin Hsu
  1. Ja
  2. Was meinst du damit? Es wird jedes Mal neu ausgeführt, wenn die Methode ausgeführt wird.
  3. Ja, der .Net-Garbage-Collector verwendet einen Algorithmus, der mit allen globalen/in-scope-Variablen beginnt, diese durchläuft, während er einer Referenz folgt, die er rekursiv findet, und alle Objekte im Speicher löscht, die als nicht erreichbar gelten. Weitere Informationen zur Garbage Collection finden Sie hier
  4. Ja, der Speicher aller in einer Methode deklarierten Variablen wird freigegeben, wenn die Methode beendet wird, da alle nicht erreichbar sind. Darüber hinaus werden alle Variablen, die deklariert, aber nie verwendet werden, vom Compiler optimiert, sodass Ihre Foo -Variable in Wirklichkeit niemals Speicher beansprucht.
  5. die using -Anweisung ruft beim Beenden einfach dispose für ein IDisposable -Objekt auf. Dies entspricht also Ihrem zweiten Aufzählungspunkt. Beide zeigen an, dass Sie mit dem Objekt fertig sind, und teilen dem GC mit, dass Sie bereit sind, es loszulassen. Das Überschreiben der einzigen Referenz auf das Objekt hat einen ähnlichen Effekt.
2

Lassen Sie uns Ihre Fragen einzeln beantworten.

  1. Ja, Sie erstellen jedes Mal ein neues Objekt, wenn diese Anweisung ausgeführt wird. Wenn Sie die Methode beenden, wird der Gültigkeitsbereich jedoch überschritten, und die Garbage Collection ist zulässig.
  2. Nun, das wäre das Gleiche wie # 1, außer dass Sie einen String-Typ verwendet haben. Ein String-Typ ist unveränderlich und Sie erhalten jedes Mal ein neues Objekt, wenn Sie eine Zuweisung vornehmen.
  3. Ja, der Garbage Collector sammelt Objekte außerhalb des Gültigkeitsbereichs, es sei denn, Sie weisen das Objekt einer Variablen mit einem großen Gültigkeitsbereich zu, z. B. einer Klassenvariablen.
  4. Ja.
  5. Die using-Anweisung gilt nur für Objekte, die die IDisposable-Schnittstelle implementieren. In diesem Fall eignet sich die Verwendung am besten für Objekte im Bereich einer Methode. Setzen Sie Foo o nicht in einem größeren Rahmen ein, es sei denn, Sie haben einen guten Grund, dies zu tun. Es ist am besten, den Bereich einer Variablen auf den kleinsten sinnvollen Bereich zu beschränken.
2
J Edward Ellis

Der Müllsammler kommt vorbei und räumt alles auf, was keine Referenzen mehr hat. Wenn Sie nicht verwaltete Ressourcen in Foo haben, hilft es Ihnen nicht viel, Dispose aufzurufen oder eine using-Anweisung zu verwenden.

Ich bin mir ziemlich sicher, dass dies zutrifft, da es noch in C # war. Aber ich habe einen Game-Design-Kurs mit XNA besucht und einige Zeit über den Garbage-Collector für C # gesprochen. Das Sammeln von Müll ist teuer, da Sie prüfen müssen, ob Sie Verweise auf das Objekt haben, das Sie sammeln möchten. Der GC versucht also, dies so lange wie möglich zu verschieben. Solange Ihnen also der physische Speicher nicht ausgeht, als Ihr Programm auf 700 MB aufgestockt wurde, kann es sein, dass der GC faul ist und sich noch keine Gedanken darüber macht.

Aber wenn Sie nur Foo o Außerhalb der Schleife verwenden und jedes Mal eine o = new Foo() erstellen, sollte alles gut gehen.

1
fire.eagle

Wie Brian betont, kann der GC alles sammeln, was nicht erreichbar ist, einschließlich der Objekte, die sich noch im Gültigkeitsbereich befinden, und selbst während die Instanzmethoden dieser Objekte noch ausgeführt werden. Betrachten Sie den folgenden Code:

class foo
{
    static int liveFooInstances;

    public foo()
    {
        Interlocked.Increment(ref foo.liveFooInstances);
    }

    public void TestMethod()
    {
        Console.WriteLine("entering method");
        while (Interlocked.CompareExchange(ref foo.liveFooInstances, 1, 1) == 1)
        {
            Console.WriteLine("running GC.Collect");
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
        Console.WriteLine("exiting method");
    }

    ~foo()
    {
        Console.WriteLine("in ~foo");
        Interlocked.Decrement(ref foo.liveFooInstances);
    }

}

class Program
{

    static void Main(string[] args)
    {
        foo aFoo = new foo();
        aFoo.TestMethod();
        //Console.WriteLine(aFoo.ToString()); // if this line is uncommented TestMethod will never return
    }
}

wenn TestMethod mit einem Debugbuild, mit dem angehängten Debugger oder mit der angegebenen Zeile ausgeführt wird, wird TestMethod nie zurückgegeben. Wird TestMethod jedoch ohne angeschlossenen Debugger ausgeführt, wird dies zurückgegeben.

1
Yaur