首页 > 解决方案 > WPF 应用程序中的内存分配问题(可能泄漏),使用 Pages 和 TabContol

问题描述

免责声明:

在我的应用程序中,我的内存管理存在很大问题。它们于 2018 年 5 月首次出现,但感谢 MSDN 和这里的各种文章,所以,我能够减少问题,但并没有真正解决它。在我做了一些阅读、测试和编码之后,我来到这里,因为我需要帮助。

申请背景:

我的应用是面向小客户的销售管理应用。它在远程桌面上运行,内存非常有限,因此我需要保持较低的内存使用率。MainWindow,由右侧的列表菜单组成(老式),单击菜单项后,新页面作为RadTabItem被打开(ib4 你用“Ask teleriks for that”打我,没关系,我有与 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 中打开的所有项目都是自定义页面(使用UserControl尝试过,同样的问题)并且所有项目都存在问题。KlientDzwoni()类是一个小类,提高内存使用量,大约 4 MB,但我有更大的页面,将它提高 100-200 MB(报告工具)。

说到内存和 MB,您可能会问,我是如何分析这些的,所以我从任务管理器开始,它显示,每次打开新标签时内存使用量都会增加,而且它永远不会下降。在阅读了这篇精彩的帖子后,我下载了 JetBrains dotMemory,它帮助我检测了我的应用程序中的内存泄漏,但它实际上并没有考虑不释放内存作为泄漏

在我看来,这些页面(如KlientDzwoni)由于某种原因被保存在内存中的某个位置,并且永远不会被释放并且不被认为是不需要的(因此,不会被检测为泄漏)。这是我尝试解决的问题。

我悲惨的尝试:

实际上没有帮助的是:

有很多关于内存泄漏(我仍然不确定是否是泄漏)和 TabControl 问题的帖子、文章、论文,但是,我相信它们的构造很差,因此由于以下几个原因,答案不准确:

我无法找到此问题的实际解决方案。在过去的几年里,它已经多次发布在 SO、MSDN 和其他地方,但我从来没有找到像“谢谢大家,问题是 xxx,修复是 yyy”这样的 OP 答案。但是有某种模式 - 有人带有 TabItem 内存问题,并且永远不会留下正确的答案。

那么,最后有人可以回答“如何释放分配的内存”这个问题并结束这个无休止的问题吗?

编辑2:

在此处上传示例项目:

https://1drv.ms/u/s!Ali8Cn1kitEDhEwn-WAEEl04talS 它由 Telerik 组件组成,但可以替换为 vanilla WPF,以呈现相同的行为。

标签: c#wpfmemory-leaks

解决方案


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;
    }

推荐阅读