Сведения о вопросе

Gaukhar

22:53, 27th August, 2020

Теги

.net   wpf   memory    

Предотвращение утечек памяти с помощью прикрепленных моделей поведения

Просмотров: 524   Ответов: 11

Я создал "attached behaviour" в своем приложении WPF, которое позволяет мне обрабатывать нажатие клавиши Enter и переходить к следующему элементу управления. Я называю его EnterKeyTraversal.IsEnabled, и вы можете увидеть код на моем блоге здесь .

Моя главная проблема сейчас заключается в том, что у меня может быть утечка памяти, так как я обрабатываю событие PreviewKeyDown на UIElements и никогда явно не "unhook" событие.

Как лучше всего предотвратить эту утечку (если она действительно существует)? Следует ли мне сохранить список элементов, которыми я управляю, и отцепить событие PreviewKeyDown в событии Application.Exit? Удалось ли кому-нибудь добиться успеха с привязанным поведением в своих собственных приложениях WPF и придумать элегантное решение для управления памятью?



  Сведения об ответе

prince

06:59, 5th August, 2020

Я не согласен DannySmurf

Некоторые объекты макета WPF могут засорить вашу память и сделать ваше приложение очень медленным, когда они не собираются в мусор. Поэтому я нахожу правильный выбор слов, вы просачиваете память в объекты, которые больше не используете. Вы ожидаете, что элементы будут собраны в мусор, но это не так, потому что где-то есть ссылка (в данном случае в обработчике событий from an).

Теперь для реального ответа :)

Я советую вам прочитать эту статью WPF Performance на MSDN

Не удаляются обработчики событий на объектах может сохранять объекты живыми

Делегат, которому передается объект его событие фактически является ссылкой к этому объекту. Следовательно, событие обработчики могут дольше сохранять объекты живыми даже больше, чем ожидалось. При выполнении очистки вверх по объекту, который зарегистрировался на слушайте событие объекта, оно есть необходимо удалить этот делегат прежде чем отпустить объект. Хранящий ненужные живые объекты увеличивают использование памяти приложения. Это особенно верно, когда объектом является корень логического дерева или визуальный элемент дерево.

Они советуют вам посмотреть на слабую модель событий

Другим решением было бы удалить обработчики событий, когда вы закончите работу с объектом. Но я знаю, что с присоединенными свойствами этот пункт не всегда может быть ясен..

Надеюсь, это поможет!


  Сведения об ответе

SILA

14:50, 12th August, 2020

Если отвлечься от философских дебатов, то, глядя на запись в блоге OP, я не вижу здесь никакой утечки:

ue.PreviewKeyDown += ue_PreviewKeyDown;

Жесткая ссылка на ue_PreviewKeyDown хранится в ue.PreviewKeyDown .

ue_PreviewKeyDown -это метод STATIC и не может быть GCed .

Никакая жесткая ссылка на ue не сохраняется, поэтому ничто не мешает ей быть GCed .

Так... Где же утечка информации?


  Сведения об ответе

darknet

00:10, 4th August, 2020

Да, я знаю, что в старые времена утечки памяти-это совершенно другая тема. Но с управляемым кодом новое значение термина утечка памяти может быть более подходящим...

Microsoft даже признает, что это была утечка памяти:

Зачем реализовывать шаблон WeakEvent?

Прослушивание событий может привести к утечка памяти. Типичная техника для прослушивания того или иного события стоит использовать специфичный для данного языка синтаксис, который присоединяет обработчик к событию на сервере источник. Например, в C#, году это синтаксис: source.SomeEvent += new SomeEventHandler(MyEventHandler).

Эта техника создает сильное ссылка из источника события на слушатель события. Обычно, прикрепляя обработчик событий для прослушивателя вызывает слушатель должен иметь объект продолжительность жизни, на которую влияет объект время жизни для источника (если только обработчик событий явно удаляется). Но при определенных обстоятельствах вы могли бы хотите узнать время жизни объекта слушатель, которым можно управлять только с помощью другие факторы, такие как ли это в настоящее время принадлежит к визуальному дереву заявки, а не самим заявителем время жизни источника. Всякий раз, когда срок службы исходного объекта выходит за рамки время жизни объекта прослушивателя, нормальный паттерн событий приводит к утечка памяти: слушатель сохранен живым дольше, чем предполагалось.

Мы используем WPF для клиентского приложения с большим ToolWindows, которое можно перетаскивать, все изящные вещи и все совместимо с XBAP.. Но у нас была такая же проблема с некоторыми ToolWindows, которые не были собраны в мусор.. Это было связано с тем, что он все еще зависел от слушателей событий.. Теперь это не может быть проблемой, когда вы закрываете окно и закрываете свое приложение. Но если вы создаете очень большой ToolWindows с большим количеством команд, и все эти команды повторно оцениваются снова и снова, и люди должны использовать ваше приложение в течение всего дня.. Я могу тебе сказать.. это действительно засоряет вашу память и время отклика вашего приложения..

Кроме того, мне гораздо легче объяснить своему менеджеру, что у нас произошла утечка памяти, чем объяснить ему, что некоторые объекты не являются мусором, собранным из-за некоторых событий, которые нуждаются в очистке ;)


  Сведения об ответе

+-*/

00:47, 26th August, 2020

@Nick да, проблема с привязанным поведением заключается в том, что по определению они не находятся в одном объекте с элементами, события которых вы обрабатываете.

Я думаю, что ответ лежит в использовании WeakReference каким-то образом, но я не видел никаких простых примеров кода, чтобы объяснить это мне. :)


  Сведения об ответе

SEEYOU

10:03, 8th August, 2020

Вы подумали о реализации "Weak Event Pattern" вместо обычных событий?

  1. Слабый паттерн событий в WPF
  2. Слабые Модели Событий (MSDN)


  Сведения об ответе

ЯЯ__4

21:06, 1st October, 2020

Чтобы объяснить мой комментарий к посту Джона Фентона, вот мой ответ. Давайте рассмотрим следующий пример:

class Program
{
    static void Main(string[] args)
    {
        var a = new A();
        var b = new B();

        a.Clicked += b.HandleClicked;
        //a.Clicked += B.StaticHandleClicked;
        //A.StaticClicked += b.HandleClicked;

        var weakA = new WeakReference(a);
        var weakB = new WeakReference(b);

        a = null;
        //b = null;

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

        Console.WriteLine("a is alive: " + weakA.IsAlive);
        Console.WriteLine("b is alive: " + weakB.IsAlive);
        Console.ReadKey();
    }


}

class A
{
    public event EventHandler Clicked;
    public static event EventHandler StaticClicked;
}

class B
{
    public void HandleClicked(object sender, EventArgs e)
    {
    }

    public static void StaticHandleClicked(object sender, EventArgs e)
    {
    }
}

Если у вас есть

a.Clicked += b.HandleClicked;

и установите только b в null обе ссылки weakA и weakB остаются живыми! Если вы установите только a в null b остается в живых, но не a (что доказывает, что Джон Фентон ошибается, утверждая, что жесткая ссылка хранится в поставщике событий - в данном случае a).

Это приводит меня к выводу WRONG, что

a.Clicked += B.StaticHandleClicked;

это привело бы к утечке, потому что я думал, что экземпляр a будет храниться статическим обработчиком. Это не так (Проверьте мою программу). В случае статического обработчика событий или событий все происходит наоборот. Если вы пишете

A.StaticClicked += b.HandleClicked;

ссылки будут храниться в б.


  Сведения об ответе

dump

19:46, 18th August, 2020

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


  Сведения об ответе

davran

14:45, 19th August, 2020

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

То, что я думаю, что вы (и плакат в вашем блоге) на самом деле говорите здесь, на самом деле не утечка, а скорее постоянное потребление памяти. Это не одно и то же. Чтобы быть ясным, утечка памяти - это память, которая зарезервирована программой, а затем оставлена (т. е. указатель остается висящим), и которая впоследствии не может быть освобождена. Поскольку память управляется в .NET, это теоретически невозможно. Однако программа может резервировать постоянно увеличивающийся объем памяти, не позволяя ссылкам на нее выходить за пределы области действия (и становиться пригодной для сборки мусора); однако эта память не просачивается. GC вернет его в систему, как только ваша программа завершит работу.

Так. Чтобы ответить на ваш вопрос, я не думаю, что у вас действительно есть проблема здесь. У вас, конечно, нет утечки памяти, и из вашего кода я не думаю, что вам нужно беспокоиться, что касается потребления памяти. До тех пор, пока вы не убедитесь, что вы повторно назначаете этот обработчик событий, никогда не отменяя его назначения (т. е. что вы либо устанавливаете его только один раз, либо удаляете его ровно один раз за каждый раз, когда вы назначаете его), что вы, кажется, делаете, ваш код должен быть в порядке.

Похоже, что именно этот совет пытался дать вам плакат в вашем блоге, но он использовал эту тревожную работу "leak,", которая является пугающим словом, но которую многие программисты забыли о реальном значении в управляемом мире; здесь это не применимо.


  Сведения об ответе

darknet

20:04, 20th August, 2020

@Arcturus: ...

засорите свою память и сделайте свой применение действительно медленно, когда они есть не мусор собирали.

Это совершенно очевидно, и я не возражаю. Однако: ..

.

вы утечку памяти для объекта которые ты больше не используешь... потому что там есть ссылка на них.

"память выделяется программе, и эта программа впоследствии теряет возможность доступа к ней из-за ошибок логики программы" (Википедия, " утечка памяти")

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

(Простите, что я семантический нацист... может быть, я немного старомоден, но утечка имеет очень специфическое значение. Люди склонны использовать "memory leak" в наши дни, чтобы означать все, что потребляет 2 кб памяти больше, чем они хотят...)

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


  Сведения об ответе

fo_I_K

01:03, 4th August, 2020

Правда правда,

Вы, конечно, правы.. Но в этом мире рождается целое новое поколение программистов, которые никогда не будут касаться неуправляемого кода, и я действительно верю, что определения языка будут изобретаться снова и снова. Утечки памяти в WPF таким образом отличаются от скажем C/Cpp.

Или, Конечно, для моих менеджеров я назвал это утечкой памяти.. своим коллегам я говорил об этом как о проблеме производительности!

Если говорить о проблеме Matt, то это может быть проблема производительности, которую вам, возможно, придется решить. Если вы просто используете несколько экранов и делаете эти элементы управления экраном синглетными, вы можете вообще не увидеть этой проблемы ;).


  Сведения об ответе

lats

21:06, 3rd August, 2020

Ну это (менеджер немного) я, конечно, могу понять, и посочувствовать.

Но как бы Microsoft это ни называла, я не думаю, что определение "new" подходит. Это сложно, потому что мы не живем в управляемом мире 100% (хотя Microsoft любит притворяться, что мы это делаем, сама Microsoft не живет в таком мире). Когда вы говорите "утечка памяти", вы можете иметь в виду, что программа потребляет слишком много памяти (это определение пользователя), или что управляемая ссылка не будет освобождена до выхода (как здесь), или что неуправляемая ссылка не очищается должным образом (это была бы реальная утечка памяти), или что неуправляемый код, вызванный из управляемого кода, пропускает память (другая реальная утечка).

В этом случае очевидно, что означает "memory leak", хотя мы и не совсем точно определяем его значение. Но становится ужасно скучно разговаривать с некоторыми людьми, которые называют каждое чрезмерное потребление или failure-to-collect утечкой памяти; и это расстраивает, когда эти люди-программисты, которые якобы знают лучше. Я думаю, что для технических терминов очень важно иметь однозначное значение. Отладка намного проще, когда они это делают.

В любом случае. Не хочу превращать это в воздушно-сказочную дискуссию о языке. Просто говорю...


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться