Jaki jest prawidłowy sposób na uwolnienie pamięci w C#

Mam timer w C#, który wykonuje jakiś kod wewnątrz swojej metody. Wewnątrz kodu używam kilku tymczasowych obiektów.

  1. Jeśli mam coś w rodzaju Foo o = new Foo(); wewnątrz metody, czy to oznacza, że za każdym razem, gdy tyka timer, tworzę nowy obiekt i nowe odniesienie do tego obiektu?

  2. Jeśli mam string foo = null i po prostu wrzucam coś temporalnego do foo, to czy jest to to samo co wyżej?

  3. Czy garbage collector usuwa obiekt i odniesienia lub obiekty są stale tworzone i pozostają w pamięci?

  4. Jeśli po prostu zadeklaruję Foo o; i nie wskażę jej na żadną instancję, to czy nie jest ona usuwana po zakończeniu metody?

  5. Jeśli chcę mieć pewność, że wszystko zostanie usunięte, jaki jest najlepszy sposób na to:

    • z poleceniem using wewnątrz metody
    • przez wywołanie metody dispose na końcu
    • umieszczając Foo o; poza metodą timera i po prostu wykonując przypisanie o = new Foo() wewnątrz, tak więc wskaźnik do obiektu jest usuwany po zakończeniu metody, garbage collector usunie obiekt.
Author: user579674, 2011-05-20

8 answers

1.Jeśli mam coś takiego jak Foo o = new Foo (); wewnątrz metody, czy to oznacza, że za każdym razem, gdy zegar tyka, Tworzę nowy obiekt i Nowy odniesienie do tego obiektu?

Tak.

2.Jeśli mam string foo = null i wtedy dodaję coś temporalnego do foo, czy jest to to samo, co powyżej?

Jeśli pytasz, czy zachowanie jest takie samo, to tak.

3.Czy garbage collector usuwa obiekt i odniesienie lub obiekty są stale tworzone i pozostać w pamięci?

Pamięć używana przez te obiekty jest z pewnością gromadzona po tym, jak odniesienia zostaną uznane za nieużywane.

4.Jeśli tylko zadeklaruję Foo o; i nie wskażę jej na żadną instancję, czyż nie jest to usuwane po zakończeniu metody?

Nie, ponieważ żaden obiekt nie został utworzony, nie ma obiektu do zebrania(dispose to nie właściwe słowo).

5.Jeśli chcę mieć pewność, że wszystko jest usunięte, jaki jest najlepszy sposób doing it

Jeśli klasa obiektu implementuje IDisposable to z pewnością chcesz wywołać Dispose tak szybko, jak to możliwe. Słowo kluczowe using ułatwia to, ponieważ wywołuje Dispose automatycznie w bezpieczny dla WYJĄTKÓW sposób.

Poza tym, naprawdę nie ma nic innego do zrobienia, poza tym, aby przestać używać obiektu. Jeśli Referencja jest zmienną lokalną, to gdy wyjdzie poza zakres, będzie kwalifikować się do kolekcji.1 jeśli jest zmienna poziomu klasy może być konieczne przypisanie null do niej, aby kwalifikowała się, zanim zawierająca ją klasa będzie kwalifikowalna.


1jest to technicznie niepoprawne (lub przynajmniej trochę mylące). Obiekt może kwalifikować się do kolekcji na długo przed wyjściem z zakresu. CLR jest zoptymalizowany pod kątem gromadzenia pamięci, gdy wykryje, że odniesienie nie jest już używane. W skrajnych przypadkach CLR może zebrać obiekt nawet wtedy, gdy jedna z jego metod jest nadal wykonuję!

Update:

Oto przykład, który pokazuje, że GC będzie zbierać obiekty, nawet jeśli nadal mogą być w zasięgu. Musisz skompilować Release build i uruchomić to poza debuggerem.

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");
    }
}

Na mojej maszynie finalizer jest uruchomiony podczas SomeMethod jest nadal wykonywany!

 30
Author: Brian Gideon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-21 01:25:31

. NET garbage collector zajmuje się tym wszystkim za Ciebie.

Jest w stanie określić, kiedy obiekty nie są już odwoływane i (ostatecznie) uwolni przydzieloną im pamięć.

 15
Author: Yuck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-10 13:38:50

Obiekty są kwalifikowane do zbierania śmieci, gdy wyjdą poza zakres stają się nieosiągalne (dzięki ben!). Pamięć nie zostanie uwolniona, chyba że Śmieciarz uwierzy, że kończy ci się pamięć.

Dla zarządzanych zasobów, garbage collector będzie wiedział, kiedy to jest, i nie musisz nic robić.

Dla niezarządzanych zasobów (takich jak połączenia z bazami danych lub otwartymi plikami) garbage collector nie ma możliwości sprawdzenia, ile pamięci zużywają, i dlatego musisz zwolnić je ręcznie (używając dispose, lub znacznie lepiej używając bloku)

Jeśli obiekty nie są zwalniane, albo masz dużo pamięci i nie ma takiej potrzeby, albo zachowujesz odniesienie do nich w swojej aplikacji, a zatem garbage collector ich nie uwolni (w przypadku, gdy faktycznie użyjesz tego odniesienia, które utrzymałeś)

 5
Author: Martin Booth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-20 00:25:16
  1. Tak
  2. Co masz na myśli mówiąc to samo? Zostanie ona ponownie wykonana za każdym razem, gdy metoda zostanie uruchomiona.
  3. Tak, moduł. Net garbage collector używa algorytmu, który zaczyna się od dowolnych zmiennych globalnych/in-scope, przemierza je, podążając rekurencyjnie za dowolnym odniesieniem, i usuwa dowolny obiekt w pamięci uznany za nieosiągalny. zobacz tutaj, aby uzyskać więcej informacji na temat zbierania śmieci
  4. tak, pamięć wszystkich zmiennych zadeklarowanych w metodzie jest zwalniana, gdy metoda kończy się, ponieważ wszystkie są nieosiągalne. Ponadto wszelkie zmienne zadeklarowane, ale nigdy nie używane, zostaną zoptymalizowane przez kompilator, więc w rzeczywistości twoja zmienna Foo nigdy nie zajmie pamięci.
  5. Instrukcja using po prostu wywołuje dispose na IDisposable obiekcie, gdy się kończy, więc jest to równoważne z drugim podpunktem. Oba wskażą, że skończyłeś z obiektem i poinformują GC, że jesteś gotowy, aby go puścić. Nadpisanie jedynego odniesienia do obiektu będzie miał podobny efekt.
 2
Author: Gordon Gustafson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-20 00:22:51

Odpowiedzmy na twoje pytania jeden po drugim.

  1. tak, tworzysz nowy obiekt za każdym razem, gdy ta instrukcja jest wykonywana, jednak wychodzi on "poza zakres" po wyjściu z metody i kwalifikuje się do usunięcia śmieci.
  2. cóż, to będzie to samo co #1, z tym wyjątkiem, że użyłeś typu string. Typ string jest niezmienny i otrzymujesz nowy obiekt za każdym razem, gdy wykonujesz przypisanie.
  3. tak, garbage collector zbiera obiekty poza zakresem, chyba że przypiszesz obiekt do zmiennej o dużym zakresie, takiej jak zmienna klasy.
  4. Tak.
  5. Instrukcja using dotyczy tylko obiektów implementujących interfejs IDisposable. Jeśli tak jest, najlepszym rozwiązaniem jest użycie obiektów z zakresu metody. Nie umieszczaj Foo o w większym zakresie, chyba że masz ku temu dobry powód. Najlepiej jest ograniczyć zakres dowolnej zmiennej do najmniejszego zakresu, który ma sens.
 2
Author: J Edward Ellis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-20 00:38:23

Oto krótki przegląd:

  • Gdy referencje znikną, Twój obiekt prawdopodobnie zostanie wyrzucony na śmietnik.
  • możesz liczyć tylko na statystyczną kolekcję, która utrzyma rozmiar sterty w normie, pod warunkiem, że wszystkie odniesienia do śmieci naprawdę znikną. Innymi słowy, nie ma gwarancji, że konkretny obiekt zostanie kiedykolwiek zbierany.
    • wynika z tego, że Twój finalizer również nigdy nie będzie miał gwarancji, że zostanie wywołany. Unikaj finalizatorów.
  • dwa wspólne źródła przecieków:
    • procedury obsługi zdarzeń i delegatów są odniesieniami. Jeśli subskrybujesz Zdarzenie obiektu, odnosisz się do niego. Jeśli masz delegata do metody obiektu, odwołujesz się do niej.
    • zasoby niezarządzane z definicji nie są zbierane automatycznie. Do tego służy IDisposable pattern.
  • na koniec, jeśli chcesz referencji, która nie zapobiega zbieraniu obiektu, spójrz na WeakReference.

One ostatnia rzecz: jeśli zadeklarujesz Foo foo; bez przypisania go nie musisz się martwić - nic nie wyciekło. Jeśli Foo jest typem odniesienia, nic nie zostało utworzone. Jeśli Foo jest typem wartości, jest ona przydzielana na stosie i w ten sposób zostanie automatycznie oczyszczona.

 2
Author: Kevin Hsu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-20 05:20:01

Śmieciarz przyjdzie i wyczyści wszystko, co nie ma już odniesień do niego. Jeśli nie masz niezarządzanych zasobów wewnątrz Foo, wywołanie Dispose lub użycie instrukcji using na nim nie pomoże Ci zbytnio.

Jestem całkiem pewien, że to dotyczy, ponieważ to było jeszcze w C#. Ale brałem udział w kursie projektowania gier przy użyciu XNA i spędziliśmy trochę czasu rozmawiając o garbage collector dla C#. Wywóz śmieci jest drogi, ponieważ trzeba sprawdzić, czy nie ma żadnych odniesień do obiekt, który chcesz zebrać. Więc GC stara się odkładać to tak długo, jak to możliwe. Tak długo, jak nie zabrakło fizycznej pamięci, gdy twój program poszedł do 700MB, to może być po prostu GC jest leniwy i nie martwiąc się o to jeszcze.

Ale jeśli po prostu użyjesz Foo o poza pętlą i stworzysz o = new Foo() za każdym razem, wszystko powinno działać dobrze.

 1
Author: fire.eagle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-20 00:24:13

Jak wskazuje Brian, GC może zbierać wszystko, co jest nieosiągalne, w tym obiekty, które nadal są w zasięgu, a nawet gdy metody instancji tych obiektów są nadal wykonywane. rozważmy następujący kod:

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
    }
}

Jeśli zostanie uruchomiony z kompilacją debugera, z dołączonym debugerem lub z podaną linią, nieaktywny TestMethod nigdy nie powróci. Ale uruchamianie bez dołączonego debuggera TestMethod powróci.

 1
Author: Yaur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-21 01:16:15