Найти ссылки на объект во время выполнения
У меня есть объект, который живет вечно. Я удаляю все ссылки, которые я вижу, к нему после использования, но он все еще не собран. Его жизненный цикл довольно сложный, поэтому я не могу быть уверенным, что все ссылки были очищены.
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, чтобы узнать, собрал ли он ваш объект?