Подтвердить что ты не робот

Каков правильный способ освободить память в С#

У меня есть таймер в С#, который выполняет некоторый код внутри него. Внутри кода я использую несколько временных объектов.

  • Если у меня есть что-то вроде Foo o = new Foo(); внутри метода, значит ли это, что каждый раз, когда таймер тикает, я создаю новый объект и новую ссылку на этот объект?

  • Если у меня есть string foo = null, а затем я просто поместил что-то временное в foo, это то же самое, что и выше?

  • Разве сборщик мусора когда-либо удаляет объект, а ссылка или объекты постоянно создаются и остаются в памяти?

  • Если я просто объявляю Foo o; и не указываю его на какой-либо экземпляр, не является ли это, когда метод заканчивается?

  • Если я хочу, чтобы все было удалено, что это лучший способ сделать это:

    • с оператором using внутри метода
    • вызывая метод dispose в конце
    • поместив Foo o; вне метода таймера и просто сделайте присваивание o = new Foo() внутри, так что указатель на объект будет удален после завершения метода, сборщик мусора удалит объект.
4b9b3361

Ответ 1

1.Если у меня что-то вроде Foo o = new Foo(); внутри метода, делает это означает, что каждый раз, когда таймер гаснет, Я создаю новый объект и новый ссылку на этот объект?

Да.

2.Если у меня строка foo = null, а затем я просто поставлю что-то временное в foo, это то же самое, что и выше?

Если вы спрашиваете, является ли поведение одинаковым, тогда да.

3. Убирает сборщик мусора, когда-либо удаляет объект и ссылку или объекты постоянно создаются и остаться в памяти?

Память, используемая этими объектами, безусловно, собирается после того, как ссылки считаются неиспользованными.

4.Если я просто объявляю Foo o; и не указывать на какой-либо экземпляр, это не так удаляется, когда метод заканчивается?

Нет, поскольку объект не был создан, нет объекта для сбора (dispose не является правильным словом).

5.Если я хочу, чтобы все было удалено, что является лучшим способом делая это

Если класс объекта реализует IDisposable, то вы, безусловно, захотите с жадностью позвонить Dispose как можно скорее. Ключевое слово using делает это проще, потому что оно вызывает Dispose автоматически безопасным способом.

Кроме того, вам больше нечего делать, кроме как прекратить использование объекта. Если ссылка является локальной переменной, тогда, когда она выходит за пределы области видимости, она будет иметь право на сбор. 1 Если это переменная уровня класса, вам может потребоваться присвоить ей null, чтобы сделать ее право на участие в классе, имеющем право на участие.


1 Это технически некорректно (или, по крайней мере, немного вводит в заблуждение). Объект может иметь право на сбор задолго до того, как он выходит за рамки. CLR оптимизирован для сбора памяти, когда обнаруживает, что ссылка больше не используется. В крайних случаях CLR может собирать объект, даже если один из его методов все еще выполняется!

Update:

Вот пример, демонстрирующий, что GC будет собирать объекты, даже если они все еще могут быть в области видимости. Вы должны скомпилировать сборку Release и запустить ее за пределами отладчика.

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

На моей машине финализатор запускается, пока SomeMethod все еще выполняется!

Ответ 2

Сборщик мусора .NET заботится обо всем этом для вас.

Он может определить, когда объекты больше не ссылаются и будет (в конечном итоге) освободить память, которая была им предоставлена.

Ответ 3

Объекты удовлетворяются за сборку мусора, когда они выходят из области видимости становятся недоступными (спасибо ben!). Память не будет освобождена, если сборщик мусора не убедится, что у вас закончилась память.

Для управляемых ресурсов сборщик мусора будет знать, когда это будет, и вам не нужно ничего делать.

Для неуправляемых ресурсов (таких как соединения с базами данных или открытыми файлами) сборщик мусора не знает, сколько памяти они потребляют, и поэтому вам нужно освободить их вручную (используя утилиту или, что еще лучше, используя блок)

Если объекты не освобождаются, у вас осталось много памяти, и нет необходимости, или вы поддерживаете ссылку на них в своем приложении, и, следовательно, сборщик мусора не освободит их (если вы действительно используете эта ссылка, которую вы поддерживали)

Ответ 4

  • Да
  • Что вы подразумеваете под этим? Он будет повторно выполняться каждый раз при запуске метода.
  • Да, сборщик мусора .Net использует алгоритм, который запускается с любыми глобальными переменными/в области видимости, обходит их, следуя любой ссылке, которую он находит рекурсивно, и удаляет любой объект в памяти, который считается недостижимым. подробнее см. в разделе Сбор мусора
  • Да, память из всех переменных, объявленных в методе, освобождается при выходе из метода, поскольку все они недостижимы. Кроме того, любые переменные, объявленные, но никогда не используемые, будут оптимизированы компилятором, поэтому на самом деле ваша переменная Foo никогда не будет занимать память.
  • оператор using просто вызывает dispose на объекте IDisposable, когда он завершает работу, поэтому это эквивалентно вашей второй маркерной точке. Оба будут указывать, что вы закончили с объектом и сообщите GC, что вы готовы отпустить его. Перезапись единственной ссылки на объект будет иметь аналогичный эффект.

Ответ 5

Позвольте ответить на ваши вопросы один за другим.

  • Да, вы создаете новый объект всякий раз, когда этот оператор выполняется, однако он выходит из поля при выходе из метода и имеет право на сбор мусора.
  • Ну, это будет то же самое, что и # 1, за исключением того, что вы использовали тип строки. Строковый тип неизменен, и каждый раз, когда вы выполняете задание, вы получаете новый объект.
  • Да сборщик мусора собирает объекты вне области видимости, если вы не назначаете объект переменной с большой областью, такой как переменная класса.
  • Да.
  • Оператор using применяется только к объектам, реализующим интерфейс IDisposable. Если это так, то во всех смыслах использование лучше всего подходит для объектов в пределах области метода. Не помещайте Foo o в большем объеме, если у вас нет веских оснований для этого. Лучше всего ограничить область действия любой переменной наименьшей областью, которая имеет смысл.

Ответ 6

Вот краткий обзор:

  • Как только ссылки исчезнут, ваш объект, скорее всего, будет собран в мусор.
  • Вы можете рассчитывать только на статистическую сборку, которая сохраняет ваш размер кучи обычным, если все ссылки на мусор действительно исчезли. Другими словами, нет гарантии, что конкретный объект когда-либо будет собираться в мусор.
    • Из этого следует, что ваш финализатор также никогда не будет гарантированно вызван. Избегайте финализаторов.
  • Два общих источника утечек:
    • Обработчики событий и делегаты являются ссылками. Если вы подписаны на событие объекта, вы ссылаетесь на него. Если у вас есть делегат метода объекта, вы ссылаетесь на него.
    • Неуправляемые ресурсы, по определению, не собираются автоматически. Это шаблон IDisposable для.
  • Наконец, если вам нужна ссылка, которая не мешает собирать объект, посмотрите на WeakReference.

Последнее: если вы объявляете Foo foo; без его назначения, вам не нужно беспокоиться - ничего не просачивается. Если Foo является ссылочным типом, ничего не было создано. Если Foo является типом значения, он выделяется в стеке и, следовательно, автоматически очищается.

Ответ 7

Сборщик мусора придет и очистит все, что больше не имеет ссылок на него. Если у вас нет неуправляемых ресурсов внутри Foo, вызов Dispose или использование инструкции using на нем вам не очень поможет.

Я уверен, что это применимо, поскольку он все еще находился на С#. Но я взял курс дизайна игры, используя XNA, и мы потратили некоторое время на сборщик мусора для С#. Сбор мусора дорогой, так как вам нужно проверить, есть ли у вас какие-либо ссылки на объект, который вы хотите собрать. Таким образом, GC пытается отложить это как можно дольше. Итак, до тех пор, пока у вас не закончилась физическая память, когда ваша программа пошла на 700 МБ, может быть, GC был ленив и не беспокоился об этом.

Но, если вы просто используете Foo o вне цикла и каждый раз создаете o = new Foo(), все должно хорошо работать.

Ответ 8

Как указывает Брайан, GC может собирать все, что недоступно, включая объекты, которые все еще находятся в области видимости, и даже в то время как экземпляры методов этих объектов все еще выполняются. рассмотрите следующий код:

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

если он запущен с отладочной сборкой, при подключенном отладчике или с указанной линией без комментария TestMethod никогда не вернется. Но запуск без отладчика, подключенного TestMethod, вернется.