Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
При использовании элементов управления Win2D в управляемых приложениях XAML необходимо принять меры, чтобы избежать циклов подсчета ссылок, которые могут предотвратить восстановление этих элементов сборщиком мусора.
У вас есть проблема, если...
- Вы используете Win2D на языке .NET, например C# (не собственный C++)
- Вы используете один из элементов управления XAML Win2D:
- Вы подписываетесь на события в элементе управления Win2D (например,
Draw,CreateResources,SizeChanged...) - Приложение перемещается назад и вперед между несколькими страницами XAML
Если выполнены все эти условия, цикл подсчета ссылок не позволит элементу управления Win2D быть когда-либо собранным сборщиком мусора. Новые ресурсы Win2D выделяются при каждом переходе приложения на другую страницу, но старые ресурсы никогда не освобождаются, что приводит к утечке памяти. Чтобы избежать этого, необходимо добавить код для явного разрыва цикла.
Исправление
Чтобы разорвать цикл подсчета ссылок и позволить вашей странице собирать мусор:
- Подключите событие
Unloadedстраницы XAML, содержащей элемент управления Win2D. - В обработчике
UnloadedвызовитеRemoveFromVisualTreeна элементе управления Win2D - В обработчике
Unloadedосвободите любые явные ссылки на элемент управления Win2D, настроив их наnull.
Пример кода:
void page_Unloaded(object sender, RoutedEventArgs e)
{
this.canvas.RemoveFromVisualTree();
this.canvas = null;
}
Примеры работы см. на любой из демо страниц Галереи примеров.
Как выявить утечки в цикле
Чтобы проверить, правильно ли ваше приложение разрывает циклы ссылок, добавьте метод финализатора на любую из страниц XAML, содержащих контролы Win2D.
~MyPage()
{
System.Diagnostics.Debug.WriteLine("~" + GetType().Name);
}
App В конструкторе настройте таймер, который гарантирует, что сборка мусора выполняется через регулярные интервалы:
var gcTimer = new DispatcherTimer();
gcTimer.Tick += (sender, e) => { GC.Collect(); };
gcTimer.Interval = TimeSpan.FromSeconds(1);
gcTimer.Start();
Перейдите на страницу, затем на другую. Если все циклы были разорваны, вы увидите Debug.WriteLine в окне вывода Visual Studio через секунду или две.
Обратите внимание, что вызов GC.Collect нарушает производительность, поэтому вы должны удалить этот тестовый код сразу после завершения тестирования на утечки!
Кровавые подробности
Цикл возникает, когда объект A имеет ссылку на B, а B одновременно имеет ссылку на A. Или когда A ссылается на B, B ссылается на C, а C ссылается на A и т. д.
При подписке на события элемента управления XAML этот цикл является довольно неизбежным:
- Страница XAML содержит ссылки на все элементы управления, содержащиеся в нем
- Элементы управления содержат ссылки на делегаты обработчика, которые были подписаны на события.
- Каждый делегат содержит ссылку на свой целевой объект.
- Обработчики событий обычно являются методами экземпляров класса, представляющего страницу XAML, поэтому их ссылки на целевой экземпляр указывают обратно на страницу XAML, создавая цикл.
Если все участвующие объекты реализованы на платформе .NET, такие циклы не являются проблемой, поскольку .NET использует сборку мусора, и алгоритм сбора мусора может определить и освободить группы объектов, даже если они связаны в цикле.
В отличие от .NET, C++ управляет памятью путем подсчета ссылок, который не может обнаруживать и освободить циклы объектов. Несмотря на это ограничение, приложения C++ с помощью Win2D не имеют проблем, так как обработчики событий C++ по умолчанию используют слабые, а не сильные ссылки на их целевой экземпляр. Поэтому страница документа ссылается на элемент управления, а элемент управления ссылается на делегат обработчика событий, но этот делегат не ссылается обратно на страницу документа, в результате цикл не образуется.
Проблема возникает, когда компонент WinRT C++, например Win2D, используется приложением .NET:
- Страница XAML является частью приложения, поэтому использует сборку мусора
- Элемент управления Win2D реализован в C++, поэтому использует подсчет ссылок
- Делегат обработчика событий является частью приложения, поэтому использует сборку мусора и содержит надежную ссылку на целевой экземпляр.
Существует цикл, но объекты Win2D, участвующие в этом цикле, не используют сборщик мусора .NET. Это означает, что сборщик мусора не может видеть всю цепочку, поэтому он не может обнаруживать или удалять объекты. Когда это происходит, приложение должно помочь, явно разорвав цикл. Это можно сделать либо путем выпуска всех ссылок со страницы на элемент управления (как рекомендуется выше), либо путем освобождения всех ссылок из элемента управления на делегаты обработчика событий, которые могут указывать на страницу (с помощью события выгрузки страницы для отмены подписки всех обработчиков событий).
Windows developer