В процессе поиска своих утечек памяти в приложении написанном на Silverlight (последняя запись в моём личном блоге) обнаружил в классе ChildWindow ошибку, приводящюю к утечке памяти.
В WinDbg выглядит как:
ModalWindow ниже в моём примере — наследник от ChildWindow
0:000:x86> !dumpheap -mt 05e4f830
Address MT Size
07068c28 05e4f830 236
070ba900 05e4f830 236
total 0 objects
Statistics:
MT Count TotalSize Class Name
05e4f830 2 472 MLOY.MKNA.KarjaKompassi.ModalWindow
Total 2 objects
0:000:x86> !gcroot 07068c28
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
Scan Thread 5 OSTHread 1880
Scan Thread 22 OSTHread 1034
Scan Thread 23 OSTHread 1878
Scan Thread 24 OSTHread 1f28
DOMAIN(062239E0):HANDLE(Pinned):3cc12f8:Root: 07de4260(System.Object[])->
06ed1460(MLOY.MKNA.KarjaKompassi.FeedingPlan.Shell)->
06ed14dc(MS.Internal.CoreTypeEventHelper)->
06ef0c54(System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[MS.Internal.CoreTypeEventHelper+EventAndDelegate, System.Windows]])->
070ee840(System.Collections.Generic.Dictionary`2+Entry[[System.Int32, mscorlib],[MS.Internal.CoreTypeEventHelper+EventAndDelegate, System.Windows]][])->
070a68a8(MS.Internal.CoreTypeEventHelper+EventAndDelegate)->
070a6870(System.Windows.RoutedEventHandler)->
07068c28(MLOY.MKNA.KarjaKompassi.ModalWindow)
В коде выглядит как (Reflector'ом глянул в System.Windows.Controls.dll на класс ChildWindow):
private void ChildWindow_LostFocus(object sender, RoutedEventArgs e)
{
…
Application.Current.RootVisual.GotFocus += new RoutedEventHandler(this.RootVisual_GotFocus);
…
}
public void Close()
{
…
Application.Current.RootVisual.GotFocus -= new RoutedEventHandler(this.RootVisual_GotFocus);
…
}
private void SubscribeToEvents()
{
base.LostFocus += new RoutedEventHandler(this.ChildWindow_LostFocus);
}
Мы можем несколько раз подписаться (каждый раз когда окно теряет фокус), но отписываемся только один раз, когда вызываем Close()
Решение:
В методе ChildWindow_LostFocus проблемную строку заменить на пару:
Application.Current.RootVisual.GotFocus -= new RoutedEventHandler(this.RootVisual_GotFocus);
Application.Current.RootVisual.GotFocus += new RoutedEventHandler(this.RootVisual_GotFocus);
В WinDbg выглядит как:
ModalWindow ниже в моём примере — наследник от ChildWindow
0:000:x86> !dumpheap -mt 05e4f830
Address MT Size
07068c28 05e4f830 236
070ba900 05e4f830 236
total 0 objects
Statistics:
MT Count TotalSize Class Name
05e4f830 2 472 MLOY.MKNA.KarjaKompassi.ModalWindow
Total 2 objects
0:000:x86> !gcroot 07068c28
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
Scan Thread 5 OSTHread 1880
Scan Thread 22 OSTHread 1034
Scan Thread 23 OSTHread 1878
Scan Thread 24 OSTHread 1f28
DOMAIN(062239E0):HANDLE(Pinned):3cc12f8:Root: 07de4260(System.Object[])->
06ed1460(MLOY.MKNA.KarjaKompassi.FeedingPlan.Shell)->
06ed14dc(MS.Internal.CoreTypeEventHelper)->
06ef0c54(System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[MS.Internal.CoreTypeEventHelper+EventAndDelegate, System.Windows]])->
070ee840(System.Collections.Generic.Dictionary`2+Entry[[System.Int32, mscorlib],[MS.Internal.CoreTypeEventHelper+EventAndDelegate, System.Windows]][])->
070a68a8(MS.Internal.CoreTypeEventHelper+EventAndDelegate)->
070a6870(System.Windows.RoutedEventHandler)->
07068c28(MLOY.MKNA.KarjaKompassi.ModalWindow)
В коде выглядит как (Reflector'ом глянул в System.Windows.Controls.dll на класс ChildWindow):
private void ChildWindow_LostFocus(object sender, RoutedEventArgs e)
{
…
Application.Current.RootVisual.GotFocus += new RoutedEventHandler(this.RootVisual_GotFocus);
…
}
public void Close()
{
…
Application.Current.RootVisual.GotFocus -= new RoutedEventHandler(this.RootVisual_GotFocus);
…
}
private void SubscribeToEvents()
{
base.LostFocus += new RoutedEventHandler(this.ChildWindow_LostFocus);
}
Мы можем несколько раз подписаться (каждый раз когда окно теряет фокус), но отписываемся только один раз, когда вызываем Close()
Решение:
В методе ChildWindow_LostFocus проблемную строку заменить на пару:
Application.Current.RootVisual.GotFocus -= new RoutedEventHandler(this.RootVisual_GotFocus);
Application.Current.RootVisual.GotFocus += new RoutedEventHandler(this.RootVisual_GotFocus);