Проблемы с выделением памяти (возможная утечка) в приложении WPF с использованием Pages и TabContol - PullRequest
0 голосов
/ 19 февраля 2019

Отказ от ответственности:

У меня огромная проблема с управлением памятью в моем приложении.Они появились впервые в мае 2018 года, но благодаря различным статьям на MSDN и здесь, SO, я смог уменьшить проблему, но не смог ее решить.После того, как я немного прочел, протестировал и написал код, я пришел сюда, потому что мне нужна помощь.

Фон приложения:

Мое приложение - приложение для управления продажами, для маленького клиента.Он работает на удаленных рабочих столах с довольно ограниченным объемом памяти, поэтому мне нужно сохранять низкое использование памяти.MainWindow, состоит из меню списка, справа (old-schoolish), после нажатия на элемент меню, новая Страница открывается как RadTabItem (ib4 вы ударили меня «Спросите teleriks»для этого ", это не имеет значения, у меня та же проблема с содержимым TabItems), вот пример кода:

    private void KlientDzwoniTab(object sender, MouseButtonEventArgs e)
    {
        App.StronaGlowna.Cursor = Cursors.Wait;

        try
        {
            if (CheckIfTabIsOpened("Klient dzwoni") == false)
            {
                _nazwakarty = "Klient dzwoni";
                var strona = new KlientDzwoni();
                AddItem(strona, _nazwakarty);

                App.StronaGlowna.Cursor = Cursors.Arrow;
            }
            else
            {
                SelectTab("Klient dzwoni");
                App.StronaGlowna.Cursor = Cursors.Arrow;
            }
        }
        catch (Exception exception)
        ... catch block and stuff

и важная вещь, которая является функцией AddItem ():

 public void AddItem(Page strona, string header)
    {

        try
        {
            #region Close button region
            var grid = new Grid();


            var c1 = new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) };

            var c2 = new ColumnDefinition { Width = new GridLength(21) };


            grid.ColumnDefinitions.Add(c1);
            grid.ColumnDefinitions.Add(c2);



            var button = new RadButton
            {
                Padding = new Thickness(0, 0, 0, 0),
                Margin = new Thickness(5, 0, 0, 0),
                Width = 16,
                Height = 16,
                HorizontalAlignment = HorizontalAlignment.Stretch,
                VerticalAlignment = VerticalAlignment.Top,
                VerticalContentAlignment = VerticalAlignment.Top,
                Content = "x",
                FontWeight = FontWeights.Bold,
                Background = Brushes.Transparent,
                BorderThickness = new Thickness(0, 0, 0, 0),
                Tag = header


            };

            button.Click += Zamknijtab;

            var stringHeader = header.Replace(" ", "");
            stringHeader = stringHeader.Replace(":", "");
            stringHeader = stringHeader.Replace("/", "");
            stringHeader = stringHeader.Replace("-", "");
            stringHeader = stringHeader.Replace(".", "");
            stringHeader = stringHeader.Replace("_", "");

            var textBlock = new TextBlock
            {
                Text = header
            };

            // panel.Name = header;



            grid.Children.Add(textBlock);
            Grid.SetColumn(textBlock, 0);

            grid.Children.Add(button);
            Grid.SetColumn(button, 1);

          #endregion

            var content = strona.Content;

            var itemToAdd = new RadTabItem
            {
                Header = grid,
                Content = content


            };



            var klientDzwoni = strona as KlientDzwoni;
            if (klientDzwoni != null)
            {

                itemToAdd.KeyDown += klientDzwoni.F3KeyDown;
            }

            itemToAdd.SetValue(NameProperty, stringHeader);


            tabControl.Items.Add(itemToAdd);

            tabControl.SelectedItem = itemToAdd;



        }
        catch (Exception e)
        {
           ...catch stuff
        }


    }

Теперь пользователь может, очевидно, закрыть эти вкладки, нажав на событие Zamknijtab:

  public void Zamknijtab(object sender, RoutedEventArgs e)
    {

        try
        {
            var listaTabow = tabControl.Items;

            var button = sender as RadButton;

            if (button != null)
            {
                if (button.Tag != null)
                {
                    var stringHeader = button.Tag as string;
                    stringHeader = stringHeader.Replace(" ", "");
                    stringHeader = stringHeader.Replace(":", "");
                    stringHeader = stringHeader.Replace("/", "");
                    stringHeader = stringHeader.Replace("-", "");
                    stringHeader = stringHeader.Replace(".", "");
                    stringHeader = stringHeader.Replace("_", "");

                    var tabs = listaTabow.Cast<RadTabItem>();



                    tab = tabs.Reverse().FirstOrDefault(f => f.Name == stringHeader);
                    if (tab == null)
                    {
                        tab = tabs.Reverse().FirstOrDefault(f => f.Name == stringHeader);
                    }

                    if (tab != null)
                    {
                        if (button.Tag.ToString().Contains("Zam:") &&
                            !button.Tag.ToString().Contains("podsumowanie"))
                        {

                            NumerZamowienia = stringHeader.Replace("Zam", "");
                            if (App.ZamowieniaCommitGet(NumerZamowienia))
                            {

                                var textBox = new Label
                                {
                                    Content =
                                        "Na zamówieniu są wprowadzone pozycje, czy chcesz usunąć zamówienie?!",
                                    FontWeight = FontWeights.Bold,
                                    Foreground = Brushes.Red
                                };




                                RadWindow.Confirm(new DialogParameters
                                {

                                    Header = "Potwierdź zamknięcie okna",
                                    Content = textBox,
                                    Closed = OnConfirmClosed,
                                    Owner = App.StronaGlowna,
                                    OkButtonContent = "Tak",
                                    CancelButtonContent = "Nie"

                                });


                            }
                            else
                            {
                                if (NumerZamowienia != null)
                                {

                                    var zamowienie = Db.dst_Orders.FirstOrDefault(f => f.Numer == NumerZamowienia);
                                    if (zamowienie != null)
                                    {
                                        ZamowienieId = zamowienie.Id_Order;
                                    }

                                    var pozycje = Db.dst_OrderLines.Where(f =>
                                        f.Order_Id == ZamowienieId && f.Ilosc != null && f.Ilosc > 0);
                                    if (!pozycje.Any())
                                    {
                                        var id = App.GetUserId();
                                        Db.No_Order(ZamowienieId, null, id);
                                    }
                                    tabControl.Items.Remove(tab);


                                }
                            }
                        }
                        else
                        {
                            tabControl.Items.Remove(tab);
                        }

                    }

                }
                else
                {
                    var typ = tabControl.SelectedItem as RadTabItem;
                    var tabs = listaTabow.Cast<RadTabItem>();
                    if (typ != null)
                    {
                        tab = tabs.Reverse().FirstOrDefault(f => f.Name == typ.Name);

                        if (tab != null)
                        {
                            tabControl.Items.Remove(tab);

                        }
                    }
                }               
            }
            else
            {
                var typ = tabControl.SelectedItem as RadTabItem;
                var tabs = listaTabow.Cast<RadTabItem>();
                if (typ != null)
                {
                    tab = tabs.Reverse().FirstOrDefault(f => f.Name == typ.Name);

                    if (tab != null)
                    {
                        tabControl.Items.Remove(tab);

                    }
                }
            }



        }
        catch (Exception exception)
        {
           ... Another catch

Этот, возможно, был бы немного переработан, но это так, как есть.

Проблема:

Закрытие вкладок, не освобождает выделенную им память (как никогда).

ВсеМои элементы, открытые в RadTabItems, являются пользовательскими Pages (пробовал с UserControl , та же проблема), и проблема существует для всех них.Класс KlientDzwoni() - это маленький класс, увеличивающий использование памяти примерно на 4 МБ, но у меня гораздо большие страницы, которые увеличивают его на 100-200 МБ (средства отчетности).

Говоря о памяти и МБ, вы можете спросить, как я их проанализировал, поэтому я начал с ofc, диспетчера задач, и он показывает, что использование памяти увеличивается, каждый раз, когда я открываю новую вкладку, и никогдаидет вниз.Прочитав этот замечательный пост , я скачал JetBrains dotMemory, и он помог мне обнаружить утечки памяти в моем приложении, НО на самом деле это не вызывает не освобождение памяти как утечку ,

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

Мои жалкие попытки:

  • Ссылаясь на статью, упомянутую ранее, я пыталсяиспользуйте GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);.На самом деле это немного помогает (уменьшает утечку на ~ 10-20%), но не решает проблему полностью.Кроме того, так как не рекомендуется заставлять GC работать, я немного напуган.
  • BindingOperations.ClearBinding - я думал, что эта идея довольно крутая.Я имею в виду, что я не отменяю привязку событий и свойств на своих страницах, во-первых, потому что у меня нет правильного события для этого (ну, нет события закрытия, есть Unloaded с другой стороны), а во-вторых, это C #поэтому я ожидаю, что Dispatcher и GC сделают эту работу за меня.Может быть, я совершенно не прав по этому поводу, и я должен отписаться от всех событий и аннулировать все коллекции и переменные в каком-либо событии, но когда TabItems удаляется, его содержимое (Page.Content) удаляется, поэтому не должнобыть любым событием, отменить подписку, верно?
  • Обнулять переменные и свойства, такие как TabItem.Content, переменные, объекты страницы и т. д. 0 результатов.
  • Использование UserControl Я упоминал выше, это совсем не помогает.
  • Отказ от моего любимого фреймворка, которым является Telerik (это не так), и использование TabItems вместо RadTabItems, 0 результатов.

Что было на самом деле бесполезно:

Есть много постов, статей, тезисов, о памяти Утечки (Я все еще не уверенесли это утечка) и проблемы TabControl, но, я думаю, они плохо сконструированы, поэтому ответы неточны по нескольким причинам:

  • Многие ответы сосредоточены на «утечке памяти», с идеями, как проверить, является ли это утечкой памяти, или нет, или это, я не знаю, абрикос.Суть этой проблемы в том, что память выделяется и никогда не освобождается.Мне все равно, как мы называем эту проблему.
  • Другие ответы связывают вопросы / сообщения / статьи / ответы с одинаковыми, повторяющимися вопросами. Чаще всего эта ссылка используется .

Мне не удалось найти реальное решение этой проблемы.Он был опубликован на SO, MSDN и в других местах, много раз, за ​​последние несколько лет, но я так и не нашел OP-ответ типа "спасибо, ребята, проблема была в xxx, исправление было в yyy".Но есть какая-то закономерность: кто-то приходит с проблемами с памятью TabItem и никогда не уходит с правильным ответом.

Итак, кто-то может, наконец, ответить на вопрос «Как освободить выделенную память» и решить эту бесконечную проблему?

РЕДАКТИРОВАТЬ 2:

Загруженный пример проекта здесь:

https://1drv.ms/u/s!Ali8Cn1kITEDhEwn-WAEEl04talS Он состоит из компонентов telerik, но может быть заменен на vanilla WPF для представления того же поведения.,

1 Ответ

0 голосов
/ 21 февраля 2019

После публикации этой проблемы на MSDN , кто-то действительно ответил на нее.Таким образом, решение состояло в том, чтобы отписать события от кнопки, очистить элемент вкладки и вызвать GC, вот код, который исправил мою проблему:

public void Zamknijtab(object sender, RoutedEventArgs e)
    {
        var listaTabow = tabControl.Items;
        var button = sender as RadButton;
        if (button != null)
        {
            button.Click -= Zamknijtab;
            var typ = tabControl.SelectedItem as RadTabItem;
            var tabs = listaTabow.Cast<RadTabItem>();
            if (typ != null)
            {
                tab = tabs.Reverse().FirstOrDefault(f => f.Name == typ.Name);
                if (tab != null)
                {
                    tabControl.Items.Remove(tab);
                    tab = null;
                }
            }
            GC.Collect();

        }
        else
        {
            var typ = tabControl.SelectedItem as RadTabItem;
            var tabs = listaTabow.Cast<RadTabItem>();
            if (typ != null)
            {
                tab = tabs.Reverse().FirstOrDefault(f => f.Name == typ.Name);

                if (tab != null)
                {
                    tabControl.Items.Remove(tab);
                }
            }
        }
        App.main.Cursor = Cursors.Arrow;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...