【问题标题】:Memory leak in Windows 8.1Windows 8.1 中的内存泄漏
【发布时间】:2014-04-09 12:50:32
【问题描述】:

我已经构建了自己的控件来模仿 Windows 8 中的 VariableSizedGridView。因为该控件不支持 UI 虚拟化,所以我使用虚拟化对其进行了重建。该控件基本上测量所有元素并计算它们的位置,但只绘制那些在视图中的元素。这极大地提升了 Surface RT 的性能。

但有一个问题,控件泄漏内存。我确信它是控件(或与之相关的东西),因为我已经能够在一个隔离的应用程序中重现泄漏。控件的源代码分为两个类。 VirtualizedList 类是控件的基础,并定义了几个 DependencyProperties。 VirtualizedVariableSizedWrapGrid 类是魔法发生的地方,也是在屏幕上绘制项目的地方。

我不确定如何解决这个问题,因为它已经发生了几个月,但我没有做任何帮助。我检查了我的事件,尝试处理 UI 元素(这似乎是不可能的)。我猜发生泄漏是因为 DataTemplate 的一些子元素仍然绑定到数据对象,因此仍然引用它们,这就是 UI 对象仍然留在内存中的原因。不过我可能完全错了。正如您在代码中看到的,当我更新视图时,我注释掉了RemoveChild(old);。那是因为当我重用旧的 FrameworkElements(从 DataTemplate 创建)时,泄漏会变慢。当我一直在重新创建新项目时,每次滚动操作泄漏大约 5MB。

对此的任何帮助将非常感激!谢谢。

PS:好像我不能附上所有的源代码。 VirtualizedList 类只定义了一些 DependencyProperties,所以我将把它省略掉。

来源 VirtualizedVariableSizedWrapGrid

[TemplatePart(Name="Root", Type=typeof(Grid))]
[TemplatePart(Name="Scroll", Type=typeof(ScrollViewer))]
[TemplatePart(Name="LayoutArea", Type=typeof(Grid))]
[TemplatePart(Name="SnappedView", Type=typeof(ListView))]
public class VirtualizedVariableSizedWrapGrid : VirtualizedList
{
    public static readonly DependencyProperty SnappedItemContainerStyleProperty = DependencyProperty.Register("SnappedItemContainerStyle", typeof(Style), typeof(VirtualizedVariableSizedWrapGrid), new PropertyMetadata(null, SnappedItemContainerStyleChanged));
    public static readonly DependencyProperty ListStateProperty = DependencyProperty.Register("ListState", typeof(ViewState), typeof(VirtualizedVariableSizedWrapGrid), new PropertyMetadata(ViewState.Full, ListStateChanged));

    private readonly List<object> _currentDataView;
    private Grid _root;
    private Panel _panel;
    private ScrollViewer _scrollViewer;
    private ListView _snappedView;
    private const double COLUMNS_PRELOADED = 2;
    private const double CARD_MARGIN = 6.0;
    private int _maxRows = 0;
    private object _selectedItem;
    private bool _moreDataRequested = false;

    public delegate void CalculatingItemSizeEventHandler(ItemContainer item);
    public event CalculatingItemSizeEventHandler OnCalculatingItemSize;
    public event SelectionChangedEventHandler SelectionChanged;
    public event EventHandler DataRequested;

    public enum ViewState
    {
        Full,
        Snapped
    }

    public VirtualizedVariableSizedWrapGrid()
    {
        _currentDataView = new List<object>();
        DefaultStyleKey = typeof(VirtualizedVariableSizedWrapGrid);
        this.Style = (Style) XamlReader.Load(
            @"<Style xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" xmlns:controls=""using:MeTweets.Controls"" TargetType=""controls:VirtualizedVariableSizedWrapGrid"">
                <Setter Property=""Template"">
                    <Setter.Value>
                        <ControlTemplate TargetType=""controls:VirtualizedVariableSizedWrapGrid"">
                            <Grid x:Name=""Root"" VerticalAlignment=""Stretch"" HorizontalAlignment=""Stretch"">
                                <ScrollViewer x:Name=""Scroll"" ZoomMode=""Disabled"" HorizontalScrollMode=""Auto"" VerticalScrollMode=""Disabled"" HorizontalScrollBarVisibility=""Hidden"" VerticalScrollBarVisibility=""Hidden"" HorizontalAlignment=""Stretch"" VerticalAlignment=""Stretch"">
                                    <Grid x:Name=""LayoutArea"" />
                                </ScrollViewer>
                                <ListView x:Name=""SnappedView"" HorizontalContentAlignment=""Stretch""  HorizontalAlignment=""Stretch"" />
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>");

        this.Loaded += VirtualizedVariableSizedWrapGrid_Loaded;
    }

    ~VirtualizedVariableSizedWrapGrid()
    {
        this.Loaded -= VirtualizedVariableSizedWrapGrid_Loaded;
    }

    #region Dependency Property Changed
    private static void SnappedItemContainerStyleChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        VirtualizedVariableSizedWrapGrid varSizeGrid = sender as VirtualizedVariableSizedWrapGrid;
        if (varSizeGrid == null || !(e.NewValue is Style))
            return;

        varSizeGrid.SetupSnappedView();
    }

    private static void ListStateChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        VirtualizedVariableSizedWrapGrid varSizeGrid = sender as VirtualizedVariableSizedWrapGrid;
        if (varSizeGrid == null || e.NewValue.Equals(e.OldValue))
            return;

        if (varSizeGrid.ListState == ViewState.Full)
        {
            varSizeGrid._scrollViewer.Visibility = Visibility.Visible;
            varSizeGrid._snappedView.Visibility = Visibility.Collapsed;
        }
        else if (varSizeGrid.ListState == ViewState.Snapped)
        {
            varSizeGrid._scrollViewer.Visibility = Visibility.Collapsed;
            varSizeGrid._snappedView.Visibility = Visibility.Visible;
        }

        if (varSizeGrid.ListState == ViewState.Full)
        {
            var snapScroll = varSizeGrid._snappedView.FindFirstChildOfType<ScrollViewer>();
            if (snapScroll != null)
            {
                int index = (int)snapScroll.VerticalOffset;
                if (index >= 0 && index < varSizeGrid.ItemsSource.Count)
                    varSizeGrid.BringIntoView(varSizeGrid.ItemsSource[index]);
            }
        }
        else if (varSizeGrid.ListState == ViewState.Snapped)
        {
            if (varSizeGrid._itemContainer == null)
                return;

            var item = (from a in varSizeGrid._itemContainer
                        where (a.Column * varSizeGrid.ItemWidth) + varSizeGrid.ItemWidth > varSizeGrid._scrollViewer.HorizontalOffset + varSizeGrid.Padding.Left
                        select a).FirstOrDefault();
            if (item == null)
                return;

            varSizeGrid.BringIntoView(varSizeGrid.ItemsSource[item.Index]);
        }
    }
    #endregion

    #region Virtual Voids
    protected override void OnItemTemplateChanged(DependencyPropertyChangedEventArgs e)
    {
        Debug.WriteLine("Item template changed");
        SetupSnappedView();
        GenerateItemContainers();
    }

    protected override void OnItemsSourceChanged(DependencyPropertyChangedEventArgs e)
    {
        Debug.WriteLine("Items source changed");
        SetupSnappedView();

        if (e.OldValue != null)
        {
            if (e.OldValue is INotifyCollectionChanged)
            {
                INotifyCollectionChanged collection = (INotifyCollectionChanged)e.OldValue;
                collection.CollectionChanged -= collection_CollectionChanged;
            }

            ClearItems();
        }
        if (e.NewValue != null)
        {
            GenerateItemContainers();
            UpdateView();

            if (e.NewValue is INotifyCollectionChanged)
            {
                INotifyCollectionChanged collection = (INotifyCollectionChanged)e.NewValue;
                collection.CollectionChanged += collection_CollectionChanged;
            }
        }
    }

    protected virtual DataTemplate GetDataTemplateForItem(object item)
    {
        return ItemTemplate;
    }
    #endregion

    #region Properties
    public Style SnappedItemContainerStyle
    {
        get { return (Style)GetValue(SnappedItemContainerStyleProperty); }
        set { SetValue(SnappedItemContainerStyleProperty, value); }
    }

    public ViewState ListState
    {
        get { return (ViewState)GetValue(ListStateProperty); }
        set { SetValue(ListStateProperty, value); }
    }

    public object SelectedValue
    {
        get { return _selectedItem; }
        set
        {
            if (value == _selectedItem)
                return;

            if (value == null)
            {
                _selectedItem = null;
                return;
            }

            var oldValue = _selectedItem;

            if (BringIntoView(value))
            {
                _selectedItem = value;
                if (SelectionChanged != null)
                    SelectionChanged(this, new SelectionChangedEventArgs(new List<object>() { oldValue }, new List<object>() { value }));
            }
        }
    }
    #endregion

    #region Overrides
    protected override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        Debug.WriteLine("OnApplyTemplate");
        _root = this.GetTemplateChild("Root") as Grid;
        _panel = this.GetTemplateChild("LayoutArea") as Panel;
        _scrollViewer = this.GetTemplateChild("Scroll") as ScrollViewer;
        _snappedView = this.GetTemplateChild("SnappedView") as ListView;
        _snappedView.Loaded += _snappedView_Loaded;
        _scrollViewer.ViewChanging += _scrollViewer_ViewChanging;
        _scrollViewer.ViewChanged += _scrollViewer_ViewChanged;
        _panel.SizeChanged += _panel_SizeChanged;
        _snappedView.SelectionChanged += _snappedView_SelectionChanged;
        App.RootFrame.SizeChanged += RootFrame_SizeChanged;

        if (ItemsSource != null)
        {
            GenerateItemContainers();
            UpdateView();
        }
        SetupSnappedView();
    }
    #endregion

    #region Private voids
    protected override void ClearItems()
    {
        if (_panel == null)
            return;

        foreach (var item in _panel.Children.Cast<FrameworkElement>())
        {
            RemoveChild(item);
        }

        _currentDataView.Clear();
        _panel.Children.Clear();
    }

    protected override void GenerateItemContainers()
    {
        if (_panel == null || _panel.Visibility == Visibility.Collapsed || _panel.ActualHeight == 0 || _panel.ActualWidth == 0)
            return;

        _itemContainer.Clear();

        if (ItemsSource == null)
            return;

        int _currentRow = 0, _currentColumn = 0;
        int _currentColWidth = 1;
        _maxRows = (int)((_panel.ActualHeight-this.Padding.Bottom-this.Padding.Top) / this.ItemHeight);
        for (int ix = 0; ix < ItemsSource.Count; ix++)
        {
            var item = ItemsSource[ix];
            var container = new ItemContainer { Index = ix };
            if (_currentDataView.Contains(item))
                container.IsRealized = true;

            if (OnCalculatingItemSize != null)
                OnCalculatingItemSize(container);

            if (container.RowSpan < 0)
                container.RowSpan = 0;
            if (container.ColumnSpan < 0)
                container.ColumnSpan = 0;

            container.Column = _currentColumn;
            container.Row = _currentRow;

            if (container.RowSpan + _currentRow > _maxRows) //Not enough rows --> new column
            {
                if (_itemContainer.Count > 0)
                {
                    // Equally split the rest of the space in this column over the elements that are currently in it
                    var additionalrows = _maxRows - _currentRow;
                    var c = _itemContainer.Where(x => x.Column == _currentColumn).ToList();
                    var ccount = c.Count();
                    var i = 0;
                    foreach (var t in c)
                    {
                        t.Row += (additionalrows/ccount)*i;
                        t.RowSpan += additionalrows/ccount;
                        i++;
                    }

                    // Couldn't equally divide, give the rest of the space to the last element
                    if (additionalrows%ccount > 0)
                    {
                        c.Last().RowSpan += additionalrows%ccount;
                    }
                }

                _currentColumn++;
                container.Column = _currentColumn;
                _currentRow = 0;
                container.Row = _currentRow;
                _currentColWidth = container.ColumnSpan;
                _itemContainer.Add(container);
                _currentRow += container.RowSpan;
                continue;
            }

            //Make column as wide as widest element
            if (container.ColumnSpan > _currentColWidth)
                _currentColWidth = container.ColumnSpan;
            if (_currentColWidth > container.ColumnSpan)
                container.ColumnSpan = _currentColWidth;

            _currentRow += container.RowSpan;

            if (_currentRow >= _maxRows)
            {
                //Make sure all elements in column are as wide as widest element
                foreach (var wider in
                            _itemContainer.Where(x => x.ColumnSpan < _currentColWidth && x.Column == _currentColumn))
                {
                    wider.ColumnSpan = _currentColWidth;
                }

                _currentRow = 0;
                _currentColumn += _currentColWidth;
                _currentColWidth = 1;
            }

            _itemContainer.Add(container);
        }

    }

    protected override void UpdateView()
    {
        if (_panel == null || _scrollViewer == null || _itemContainer == null || _itemContainer.Count == 0)
            return;

        _panel.Width = ((_itemContainer.Last().Column + 1) * this.ItemWidth) + this.Padding.Left + this.Padding.Right;

        var margin = this.ItemWidth * COLUMNS_PRELOADED;
        double minLeft = (_scrollViewer.HorizontalOffset - margin);
        double maxLeft = (_scrollViewer.HorizontalOffset + _scrollViewer.ActualWidth + margin);
        int minColumn = (int)(minLeft / this.ItemWidth)-1;
        int maxColumn = (int)(maxLeft / this.ItemWidth)+1;
        minLeft = (minColumn * this.ItemWidth) + this.Padding.Left;
        maxLeft = (maxColumn * this.ItemWidth) + this.Padding.Left;

        _currentDataView.Clear();
        var shown = _itemContainer.Where(x => x.Column >= minColumn && x.Column <= maxColumn);
        _currentDataView.AddRange(shown.Select(x => ItemsSource[x.Index]));

        List<FrameworkElement> recycle = new List<FrameworkElement>();
        foreach (var old in _panel.Children.Cast<FrameworkElement>().Where(x => x.Margin.Left < minLeft || x.Margin.Left > maxLeft || 
            !(_currentDataView.Contains(x.DataContext))).ToList())
        {
            foreach (var recycled in _itemContainer.Where(x => ItemsSource[x.Index] == old.DataContext))
                recycled.IsRealized = false;
            //RemoveChild(old);
            recycle.Add(old);
        }

        foreach (var item in shown)
        {
            FrameworkElement obj = recycle.FirstOrDefault();
            double left, top;
            if (item.IsRealized)
            {
                //Rearrange item if needed
                obj = _panel.Children.FirstOrDefault(x => x is FrameworkElement && ((FrameworkElement)x).DataContext == ItemsSource[item.Index]) as FrameworkElement;
                if (obj == null)
                    continue;
                obj.Height = (this.ItemHeight * item.RowSpan) - (2 * CARD_MARGIN);
                obj.Width = (this.ItemWidth * item.ColumnSpan) - (2 * CARD_MARGIN);
                left = (item.Column * this.ItemWidth) + this.Padding.Left;
                top = (item.Row * this.ItemHeight) + this.Padding.Top;
                obj.Margin = new Thickness(left, top, 0, 0);
                continue;
            }

            item.IsRealized = true;

            if (obj == null)
            {
                obj = this.GetDataTemplateForItem(item).LoadContent() as FrameworkElement;
                obj.Tapped += Item_Tapped;
                obj.PointerReleased += Item_PointerReleased;
                obj.PointerEntered += Item_PointerEntered;
                obj.PointerExited += Item_PointerExited;
                _panel.Children.Add(obj);
            }
            else
                recycle.Remove(obj);

            obj.DataContext = null;
            obj.DataContext = ItemsSource[item.Index];
            obj.Height = (this.ItemHeight * item.RowSpan) - (2 * CARD_MARGIN);
            obj.Width = (this.ItemWidth * item.ColumnSpan) -  (2 * CARD_MARGIN);
            obj.VerticalAlignment = VerticalAlignment.Top;
            obj.HorizontalAlignment = HorizontalAlignment.Left;
            obj.Visibility = Visibility.Visible;
            left = (item.Column * this.ItemWidth) + this.Padding.Left; 
            top = (item.Row * this.ItemHeight) + this.Padding.Top;
            obj.Margin = new Thickness(left, top, 0, 0);
        }

        foreach (var notRecycled in recycle) //Remove not recycled items
            RemoveChild(notRecycled);

        Debug.WriteLine("Children count "+_panel.Children.Count);
    }

    private void RemoveChild(FrameworkElement item)
    {
        item.Tapped -= Item_Tapped;
        item.PointerReleased -= Item_PointerReleased;
        item.PointerEntered -= Item_PointerEntered;
        item.PointerExited -= Item_PointerExited;
        _panel.Children.Remove(item);
        item.DataContext = null;
        item.ClearValue(DataContextProperty);
    }

    public static IEnumerable<PropertyInfo> GetAllProperties(TypeInfo type)
    {
        var list = type.DeclaredProperties.ToList();

        var subtype = type.BaseType;
        if (subtype != null)
            list.AddRange(GetAllProperties(subtype.GetTypeInfo()));

        return list.ToArray();
    }

    public List<DependencyObject> AllChildren(DependencyObject parent)
    {
        var list = new List<DependencyObject>();
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            if (child != null)
                list.Add(child);
            list.AddRange(AllChildren(child));
        }
        return list;
    }

    private void SetupSnappedView()
    {
        if (_snappedView == null)
            return;

        _snappedView.ItemTemplate = this.ItemTemplate;
        _snappedView.ItemContainerStyle = this.SnappedItemContainerStyle;
        _snappedView.ItemsSource = this.ItemsSource;

        var snapScroll = this._snappedView.FindFirstChildOfType<ScrollViewer>();
        if (snapScroll != null)
        {
            snapScroll.ViewChanged -= snapped_ViewChanged;
            snapScroll.ViewChanged += snapped_ViewChanged;
        }
    }

    private void Item_PointerExited(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
    {

    }

    private void Item_PointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
    {

    }

    private bool _pointerPressed = false;
    private object _pointerOriginalSource = null;
    private void Item_PointerReleased(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
    {
        if (e.Handled)
            return;

        _pointerPressed = true;
        _pointerOriginalSource = e.OriginalSource;
    }

    private void Item_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
    {
        if (!_pointerPressed || e.OriginalSource != _pointerOriginalSource)
        {
            _pointerPressed = false;
            return;
        }

        object previousObject = _selectedItem;
        object dataObject = ((FrameworkElement)sender).DataContext;

        if (dataObject != _selectedItem)
        {
            _selectedItem = dataObject;
            if (SelectionChanged != null)
                SelectionChanged(this, new SelectionChangedEventArgs(new List<object>() { previousObject }, new List<object>() { dataObject }));
        }

        _pointerPressed = false;
    }

    void collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        object item = null;
        if (_itemContainer != null && _itemContainer.Count > 0 && ListState == ViewState.Snapped)
        {
            var snapScroll = _snappedView.FindFirstChildOfType<ScrollViewer>();
            if (snapScroll != null)
            {
                int index = (int)snapScroll.VerticalOffset;
                if (index >= 0 && index < ItemsSource.Count)
                    item = ItemsSource[index];
            }
        }
        else if (_itemContainer != null && _itemContainer.Count > 0 && ListState == ViewState.Full)
        {
            var i = (from a in _itemContainer
                        where (a.Column * ItemWidth) + ItemWidth > _scrollViewer.HorizontalOffset + Padding.Left
                        select a).FirstOrDefault();
            if (i == null)
                return;

            item = ItemsSource[i.Index];
        }

        if (e.Action == NotifyCollectionChangedAction.Add || e.Action == NotifyCollectionChangedAction.Reset)
            _moreDataRequested = false;

        GenerateItemContainers();
        UpdateView();

        //if (item != null)
        //    BringIntoView(item);
        //else if (this.ItemsSource is IList && (this.ItemsSource as IList).Count > 0)
        //    BringIntoView((this.ItemsSource as IList)[0]);
    }

    void VirtualizedVariableSizedWrapGrid_Loaded(object sender, RoutedEventArgs e)
    {
        if (this.ListState == ViewState.Full)
        {
            this._scrollViewer.Visibility = Visibility.Visible;
            this._snappedView.Visibility = Visibility.Collapsed;
        }
        else if (this.ListState == ViewState.Snapped)
        {
            this._scrollViewer.Visibility = Visibility.Collapsed;
            this._snappedView.Visibility = Visibility.Visible;
        }

        Debug.WriteLine("VirtualizedVariableSizedWrapGrid Loaded");
        if (this.ItemsSource != null && (_itemContainer == null || _itemContainer.Count == 0))
        {
            GenerateItemContainers();
            UpdateView();
        }
    }

    void snapped_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
    {
        if (!(sender is ScrollViewer))
            return;

        var scroll = (sender as ScrollViewer);
        var maxOffset = scroll.ExtentHeight - scroll.ViewportHeight;
        if (!_moreDataRequested && (maxOffset <= 0 || scroll.VerticalOffset >= maxOffset))
        {
            _moreDataRequested = true;
            if (DataRequested != null)
                DataRequested(this, EventArgs.Empty);
        }
    }

    void _snappedView_Loaded(object sender, RoutedEventArgs e)
    {
        var snapScroll = this._snappedView.FindFirstChildOfType<ScrollViewer>();
        if (snapScroll != null)
        {
            snapScroll.ViewChanged += snapped_ViewChanged;
        }
    }

    private double _lastOffsetUpdate = 0;
    void _scrollViewer_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
    {
        if (e.NextView.HorizontalOffset == e.FinalView.HorizontalOffset)
        {
            UpdateView();
            return;
        }

        if (Math.Abs(e.NextView.HorizontalOffset - _lastOffsetUpdate) > 100)
        {
            _lastOffsetUpdate = e.NextView.HorizontalOffset;
            UpdateView();
        }
    }

    void _scrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
    {
        if (!_moreDataRequested && this._scrollViewer.HorizontalOffset + this._scrollViewer.ActualWidth >= (this._panel.ActualWidth))
        {
            _moreDataRequested = true;
            if (DataRequested != null)
                DataRequested(this, EventArgs.Empty);
        }
    }

    void _panel_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        int currentMaxRows = _maxRows;
        _maxRows = (int)((_panel.ActualHeight - this.Padding.Bottom - this.Padding.Top) / this.ItemHeight);
        if (_maxRows != currentMaxRows) //Orientation probably changed, control got higher
        {
            Debug.WriteLine("Panel size changed");
            ClearItems();
            GenerateItemContainers();
            UpdateView();
        }
    }

    void _snappedView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems.Count > 0)
            this.SelectedValue = e.AddedItems[0];

        _snappedView.SelectedItem = null;
    }

    void RootFrame_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (_itemContainer != null && _itemContainer.Count > 0 && _panel.ActualWidth > 0)
            UpdateView();
    }
    #endregion

    #region Public voids
    public override bool BringIntoView(object obj, bool animate = true)
    {
        try 
        {
            if (_itemContainer == null || _itemContainer.Count == 0)
                return false;

            var item = (from a in _itemContainer
                        where ItemsSource[a.Index] == obj
                        select a).FirstOrDefault();
            if (item == null)
                return false;

            //Calculate position
            double offset = (item.Column * this.ItemWidth) + CARD_MARGIN;
            this._scrollViewer.ChangeView(offset, null, null, !animate);
            _snappedView.ScrollIntoView(obj, ScrollIntoViewAlignment.Leading);

            UpdateView();

            return true;
        }
        catch { return false; }
    }
    #endregion
}

【问题讨论】:

  • 欢迎来到 Stack Overflow!很多时候,如果您将代码归结为Minimal, Complete, Tested and Readable,我们会更容易找到问题。在许多情况下,锻炼甚至可以帮助您自己找到答案!

标签: c# windows windows-8 memory-leaks microsoft-metro


【解决方案1】:

您是否在某处为容器设置了ContentTemplate 和/或Style?或者可能是其他属性,例如BorderBrush 等?也许你在 xaml 中这样做?

如果这样做,请在释放容器时尝试将所有这些属性设置为null。它在一些内存泄漏的情况下帮助了我。

【讨论】:

  • 我投了反对票,因为这不是没有答案。即使这导致他摆脱了泄漏,这也只是一堆猜测,甚至无法找到正确的解决方案。我同意他的代码太长,但这与泄漏有什么关系?长代码使检测问题变得更加困难,但这应该是“长代码”第一次导致泄漏。
  • 如你所愿。要获得实际泄漏,您需要完整的解决方案。从他的代码中,您只能看到他取消了所有可能的订阅。但泄漏可能不存在。你认为这里有真正的魔术师从帽子里得到兔子吗?如果你能看懂,我的回答不是关于长代码
  • 但他确定泄漏在此控件中。我理解你的观点,但它不是一个答案(这是一堆尝试这个或尝试那个)。此信息可能很有用,但请使用评论系统 imo。不要误会我的意思,我没有投反对票,因为我喜欢投反对票。并且 Idd 你的实际“答案”不是关于太长的代码,而是你从它开始(至少提到太长的代码与他的问题相关的原因:例如,长代码使得更难定位泄漏)。但同样,这不是没有答案。如果我遇到与 OP 相同的问题,那么查看您的答案对我没有任何帮助。
  • 相信我,对此没有简单的答案。这里有很多类似的问题:stackoverflow.com/questions/2259433/…。我的回答是关于我在类似控件中的类似泄漏的经验。如果他幸运的话,这会有所帮助。如果没有,请谷歌分析和调试,做出假设并使用适当的工具进行检查。可能需要很长时间
猜你喜欢
  • 1970-01-01
  • 2015-03-02
  • 2014-11-29
  • 2015-01-03
  • 1970-01-01
  • 2014-05-03
  • 2012-10-10
  • 2021-12-11
  • 1970-01-01
相关资源
最近更新 更多