Вложение объекта ссылочного типа во внутреннюю область не влияет на сборку мусора: True или False? - PullRequest
4 голосов
/ 23 июня 2010

Я читал главу «Сборка мусора» из прекрасной книги Джеффри Рихтера «CLR via C #». Там он проиллюстрировал пример того, как GC концептуально работает (как помечены корни), ссылаясь на список разборки нативного кода, генерируемого компилятором JIT. Из этого примера мне пришло в голову, что вложенные ссылочные типы в области видимости имеют нулевой эффект для ускорения сборки мусора вложенной переменной. Интересно, правильно ли я это понимаю. В любом случае рассмотрим эти 2 версии кода:

A) Вложение переменной ссылочного типа (y) во внутреннюю область видимости:

namespace scope
{
    class A { public void foo() { } }
    class Program
    {
        static void Main(string[] args)
        {
            A x = new A();
            x.foo();
            {
                A y = new A();
                y.foo();
            }
        }
    }
}

B) То же, что и выше, за исключением того, что x и y находятся в одной и той же области видимости.

namespace scope
{
    class A { public void foo() { } }
    class Program
    {
        static void Main(string[] args)
        {
            A x = new A();
            x.foo();

            A y = new A();
            y.foo();
        }
    }
}

Из любопытства я проверил сгенерированный код IL для обеих версий, и они ЖЕ!

В1: Итак, это, похоже, подразумевает, что на самом деле определение объема не ускоряет сбор мусора. Это правильно? (Примечание: я знаю об операторе «using», но мне интересно только поведение GC «простой старой области», как показано в 2 приведенных выше примерах.)

Q2: Если ответ на вопрос Q1 «Верный», то я совершенно сбит с толку тем, как может произойти ситуация «Время жизни объекта не определяется областью действия», как описано здесь: http://www.curly -brace.com / favorite.html

Ответы [ 4 ]

8 голосов
/ 23 июня 2010

Сборка мусора частично зависит от того, работаете ли вы в отладчике или нет. Я предполагаю, что мы не.

Это может быть гораздо более агрессивно, чем вы думаете. Например:

object o = new object();
Console.WriteLine(o);   
Console.WriteLine("hi");
o = new object();

Объект можно собрать сразу после второй строки, т. Е. Задолго до того, как переменная o выйдет из области видимости. Это также было бы верно без последней строки.

Другими словами, область действия не ускоряет сборку мусора - потому что GC уже умнее, чем вы думаете.

На самом деле сборщик мусора может быть еще более агрессивным . Рассмотрим этот код:

Foo f = new Foo();
f.SomeMethod();
Console.WriteLine("Hello");

где Foo выглядит так:

public class Foo
{
    int x = 10;

    public void SomeMethod()
    {
        Console.WriteLine(x);
        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine("Hello");
            Thread.Sleep(100);
        }
    }
}

Теоретически сборщик мусора может собирать объект Foo , пока SomeMethod работает , пока он проходит первую строку, где он фактически читает из x. Если у Foo был финализатор, вы могли бы запустить финализатор в одном потоке, в то время как SomeMethod работал в другом. Страшные вещи.

5 голосов
/ 23 июня 2010

Джон, конечно, прав. Для дополнительной информации я отсылаю вас к спецификации C #, которая гласит:

Фактическое время жизни локальной переменной зависит от реализации. Например, компилятор может статически определить, что локальная переменная в блоке используется только для небольшой части этого блока. Используя этот анализ, компилятор может сгенерировать код, в результате которого срок хранения переменной будет меньше, чем у содержащего ее блока.

, а также важно:

Хранилище, на которое ссылается локальная ссылочная переменная, восстанавливается независимо от времени жизни этой локальной ссылочной переменной.

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

3 голосов
/ 23 июня 2010

Автор связанной страницы считает, что C # каким-то образом (или должен быть) связан с C ++.С такой позиции неудивительно, что за этим следуют «удивительные» (в смысле принципа наименьшего удивления) вещи.В частности,

A ReadAccessor - это объект, который обеспечивает доступ к пиксельным данным на изображении, которые по соображениям производительности хранятся в файле отображения памяти.

следует немедленно предложить (на очень базовом уровне знаний .NET), чтобы ReadAccessor реализовал IDisposable.

1 голос
/ 23 июня 2010

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

Ссылки на область действия метода будут просто полностью разыменованы к концу метода (в вашем примере).

Обновление: или, как указывает Джон, если до этого полностью разыменовывалось, это понятно для GC.

...