Найти ссылки на объект во время выполнения

У меня есть объект, который живет вечно. Я удаляю все ссылки, которые я вижу, к нему после использования, но он все еще не собран. Его жизненный цикл довольно сложный, поэтому я не могу быть уверенным, что все ссылки были очищены.

if ( container.Controls.Count > 0 )
{ 
    var controls = new Control[ container.Controls.Count ];
    container.Controls.CopyTo( controls, 0 );

    foreach ( var control in controls ) 
    { 
         container.Controls.Remove( control );
         control.Dispose();
    }

    controls = null; 
}

GC.Collect();
GC.Collect(1);
GC.Collect(2);
GC.Collect(3);

Как я могу узнать, какие ссылки у него есть? Почему он не собирается?

Ответы

Ответ 1

Попробуйте использовать профайлер памяти, (например ants), он скажет вам, что держит объект в живых. Попытка 2-го угадать, что этот тип проблемы очень тяжелый.

Red-gate дает 14-дневную пробную версию, которая должна быть более чем достаточно времени для решения этой проблемы и решить, имеет ли профилировщик памяти долгосрочное значение.

лоты других профилировщиков памяти на рынке (например, .NET Memory Profiler), большинство из них имеют но я обнаружил, что инструменты Red-Gate просты в использовании, поэтому сначала попробуйте их.

Ответ 2

Вам нужно будет использовать Windbg и Sosex расширение.

Команды !DumpHeap и !GCRoot могут помочь вам идентифицировать экземпляр и все остальные ссылки, которые поддерживают его.

Ответ 3

Я использовал .NET Memory Profiler для выполнения серьезного профилирования памяти в одном из наших проектов. Это отличный инструмент для изучения управления памятью вашего приложения. Мне не платят за эту информацию:), но это просто помогло мне.

Ответ 4

Я решил аналогичную проблему с расширением SOS (которое, видимо, больше не работает с Visual Studio 2013, но отлично работает со старыми версиями Visual Studio).

Я использовал следующий код, чтобы получить адрес объекта, для которого я хотел отслеживать ссылки:

public static string GetAddress(object o)
{
    if (o == null)
    {
        return "00000000";
    }
    else
    {
        unsafe
        {
            System.TypedReference tr = __makeref(o);
            System.IntPtr ptr = **(System.IntPtr**) (&tr);
            return ptr.ToString ("X");
        }
    }
}

а затем, в окне Visual Studio 2012, во время работы в отладчике введите:

.load C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll

который загрузит расширение SOS.dll.

Затем вы можете использовать GetAddress(x) для получения шестнадцатеричного адреса объекта (например, 8AB0CD40), а затем использовать:

!do 8AB0CD40
!GCRoot -all 8AB0CD40

чтобы выгрузить объект и найти все ссылки на объект.

Просто имейте в виду, что если GC работает, он может изменить адрес объекта.

Ответ 5

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

Однако вы можете вручную запустить коллекцию (GC.Collect()), но вам, возможно, придется ждать завершения финализаторов (GC.WaitForPendingFinalizers()). Однако делать это в производственном приложении не рекомендуется, так как это может повлиять на эффективность управления памятью (GC работает слишком часто или ждет завершения финализаторов). Если объект все еще существует, у него на самом деле все еще есть какая-то живая ссылка.

Ответ 6

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

Какие средства вы используете для проверки GC, чтобы узнать, собрал ли он ваш объект?