Отличается ли поведение GC
От: dsbalbor  
Дата: 08.02.15 18:21
Оценка: :)
Уважаемые друзья,
извините, если вопрос покажется простоват. Отличается ли поведение сборщика мусора в .NET в следующих случаях (C# приложение):
1.
{
...
   Class1 cls1 = getClass1();
   Class2 cls2 = cls1.getClass2();
   Class3 cls3 = cls2.getClass3();
   cls3.someMethod();
...
}


и вот такого

2.
{
...
   getClass1().getClass2().getClass3().someMethod();
...
}

Заранее спасибо за любой полезный совет.
Отредактировано 14.02.2015 17:44 VladD2 . Предыдущая версия .
Re: Отличается ли поведение GC
От: TK Лес кывт.рф
Дата: 08.02.15 19:17
Оценка: 20 (3) +2
Здравствуйте, dsbalbor, Вы писали:

D>Заранее спасибо за любой полезный совет.


Отличия будут в debug/release конфигурациях (в debug версии время жизни объектов будет продлено до выхода из scope).
В release версии объект может быть собран сразу после факта его последнего использования (т.е. даже на this расчитывать нельзя)
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re: Отличается ли поведение GC
От: c-smile Канада http://terrainformatica.com
Дата: 08.02.15 21:49
Оценка: 4 (2)
Здравствуйте, dsbalbor, Вы писали:

Оптимизирующий компилятор сведет первый случай ко второму.

А вообще же объекты на стеке являются GC protected.

Без оптимизации для исполнения этого блока
{
  Class1 cls1 = getClass1();
  Class2 cls2 = cls1.getClass2();
  Class3 cls3 = cls2.getClass3();
  cls3.someMethod();
}

нужно три элемента стека.

А для этого
getClass1().getClass2().getClass3().someMethod();

один элемент.
Re: Отличается ли поведение GC
От: Sinix  
Дата: 09.02.15 08:08
Оценка: 14 (4)
Здравствуйте, dsbalbor, Вы писали:

D>Заранее спасибо за любой полезный совет.

Выше уже ответили, но самый простой способ разобраться — запустить без отладчика -debug и -release сборки
  вот этого кода:
using System;
using System.Runtime.CompilerServices;
using System.Threading;

public class Program
{
    private static readonly string offset = new string('\t', 4);
    class A
    {
        public A()
        {
            Console.WriteLine(offset + "new A()");
        }
        ~A()
        {
            Console.WriteLine(offset + "~A()");
        }
        public B GetB() { return new B(); }
    }
    class B
    {
        public B()
        {
            Console.WriteLine(offset + "new B()");
        }
        ~B()
        {
            Console.WriteLine(offset + "~B()");
        }
        public C GetC() { return new C(); }
    }
    class C
    {
        public C()
        {
            Console.WriteLine(offset + "new C()");
        }
        ~C()
        {
            Console.WriteLine(offset + "~C()");
        }
        public D GetD() { return new D(); }
    }
    class D
    {
        public D()
        {
            Console.WriteLine(offset + "new D()");
        }
        ~D()
        {
            Console.WriteLine(offset + "~D()");
        }
    }

    private static void FullGC(bool inMethod)
    {
        var oldColor = Console.ForegroundColor;
        try
        {
            Console.ForegroundColor = inMethod ? ConsoleColor.Green : ConsoleColor.Red;
            Console.WriteLine("\tRun GC {");

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();

            Console.WriteLine("\t}");
        }
        finally
        {
            Console.ForegroundColor = oldColor;
        }
    }

    static void Main(string[] args)
    {
        AsVariables();
        FullGC(false);
        Console.WriteLine("------------");
        Console.WriteLine("");

        AsVariablesNull();
        FullGC(false);
        Console.WriteLine("------------");
        Console.WriteLine("");

        CallChain();
        FullGC(false);
        Console.WriteLine("------------");
        Console.WriteLine("");

        Console.Write("Done.");
        Console.ReadKey();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void AsVariables()
    {
        Console.WriteLine("-> Enter AsVariables()");
        var a = new A();
        var b = a.GetB();
        var c = b.GetC();
        var d = c.GetD();

        FullGC(true);

        Console.WriteLine("<-Exit AsVariables()");
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void AsVariablesNull()
    {
        Console.WriteLine("-> Enter AsVariablesNull()");
        var a = new A();
        var b = a.GetB();
        var c = b.GetC();
        var d = c.GetD();

        a = null;
        b = null;
        c = null;
        d = null;

        FullGC(true);

        Console.WriteLine("<- Exit AsVariablesNull()");
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void CallChain()
    {
        Console.WriteLine("-> Enter CallChain()");
        var d = new A().GetB().GetC().GetD();

        FullGC(true);

        Console.WriteLine("<- Exit CallChain()");
    }
}


Дальше открываем ilspy/reflector или любой декомпилятор по вкусу и смотрим на разницу в il.

Самое интересное — поведение release-версии под отладчиком.
Я на него наткнулся года три назад, у нас был хитрый баг, который с трудом, но воспроизводился на релизной сборке. И в принципе не ловился на той же сборке под отладчиком.

Вот схема возможных раскладов, нас интересует верхний правый угол. В моём случае "D" не был унаследован от CriticalFinalizerObject, что иногда приводило к неправильному порядку вызова деструкторов.
Re[2]: Отличается ли поведение GC
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 09.02.15 23:10
Оценка: 1 (1) +1
Здравствуйте, c-smile, Вы писали:

CS>А вообще же объекты на стеке являются GC protected.


Только в первом приближении.
... << RSDN@Home 1.0.0 alpha 5 rev. 0 on Windows 8 6.2.9200.0>>
AVK Blog
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.