Проблема с утечкой памяти при привязке данных в WPF UserControls

#c# #wpf #memory-leaks

#c# #wpf ( ВП ) #утечки памяти #wpf

Вопрос:

Я использую Data-binding int следующим образом с DependencyProperty, поскольку он не может освободить память при открытии новой формы, и при повторном открытии той же формы объем памяти, занимаемый формой, увеличивается.

.Xaml файл в виде

 <Border x:Name="brdMain" BorderThickness="10" BorderBrush="{Binding ElementName=UC, Path=BorderColor}" CornerRadius="5">
        <Grid x:Name="imgGrid">
            <Image x:Name="imgMain" Source="{Binding ElementName=UC, Path=ImgMain}" Stretch="{Binding ElementName=UC, Path=ImgStretch}"></Image>
            <InkCanvas EditingMode="Select" Background="#00000000" x:Name="inkCanvas" Width="{Binding ElementName=UC,Path=imgGrid}" Visibility="{Binding ElementName=UC, Path=InkCanvasVisibility}">
                <Image Name="imgVirtualProp" Stretch="Fill" Width="150" Height="100" Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center"></Image>
            </InkCanvas>
        </Grid>
    </Border>

    <StackPanel Grid.Row="1">
        <Image x:Name="imgShadow" Grid.Row="1" Stretch="Fill" Source="/PhotoBoothAgile;component/Images/Assets/Shadow.png" Width="{Binding ElementName=imgMain, Path=Width}"></Image>
        <Label x:Name="labelText" Content="{Binding ElementName=UC, Path=LabelText}" HorizontalAlignment="Center" Foreground="Wheat"></Label>
    </StackPanel>
  

Свойства файла .CS выглядят следующим образом

 public ImageWithCanvasUC()
    {
        InitializeComponent();
    }

    public ImageSource ImgMain
    {
        get { return (ImageSource)GetValue(ImageProperty); }
        set { SetValue(ImageProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Image.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ImageProperty =
        DependencyProperty.Register("ImgMain", typeof(ImageSource), typeof(ImageWithCanvasUC), new UIPropertyMetadata(null));

    public string LabelText
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Text.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("LabelText", typeof(string), typeof(ImageWithCanvasUC), new UIPropertyMetadata(""));

    //ImgStretch


    public static readonly DependencyProperty ImgStreacthProperty =
        DependencyProperty.Register("ImgStretch", typeof(Stretch), typeof(ImageWithCanvasUC), new UIPropertyMetadata(Stretch.Fill));

    public Stretch ImgStretch
    {
        get { return (Stretch)GetValue(ImgStreacthProperty); }
        set { SetValue(ImgStreacthProperty, value); }
    }

    //InkCanvVisibility

    public static readonly DependencyProperty ImgInkCanvasVisibiltyProperty =
        DependencyProperty.Register("InkCanvasVisibility", typeof(Visibility), typeof(ImageWithCanvasUC), new UIPropertyMetadata(Visibility.Hidden));

    public Visibility InkCanvasVisibility
    {
        get { return (Visibility)GetValue(ImgInkCanvasVisibiltyProperty); }
        set { SetValue(ImgInkCanvasVisibiltyProperty, value); }
    }

    public Brush BorderColor
    {
        get { return (Brush)GetValue(BorderColorProperty); }
        set { SetValue(BorderColorProperty, value); }
    }
    public static readonly DependencyProperty BorderColorProperty =
        DependencyProperty.Register("BorderColor", typeof(Brush), typeof(ImageWithCanvasUC), new UIPropertyMetadata(Brushes.BurlyWood));

    private void UC_Unloaded(object sender, RoutedEventArgs e)
    {
        imgGrid.ClearValue(Grid.DataContextProperty);
        imgMain.ClearValue(Image.SourceProperty);
        imgMain.ClearValue(Image.StretchProperty);
        labelText.ClearValue(Label.ContentProperty);
        brdMain.ClearValue(Border.BorderBrushProperty);
        imgVirtualProp.ClearValue(Image.StretchProperty);
        imgVirtualProp.ClearValue(Image.SourceProperty);
        imgShadow.ClearValue(Image.SourceProperty);
        imgShadow.ClearValue(Image.StretchProperty);
        inkCanvas.ClearValue(InkCanvas.DataContextProperty);

        BindingOperations.ClearAllBindings(imgGrid);
        BindingOperations.ClearAllBindings(imgMain);
        BindingOperations.ClearAllBindings(labelText);
        BindingOperations.ClearAllBindings(imgVirtualProp);
        BindingOperations.ClearAllBindings(imgShadow);
        BindingOperations.ClearAllBindings(brdMain);
        BindingOperations.ClearAllBindings(inkCanvas);
    }
  

Комментарии:

1. В .NET память освобождается не сразу, когда на объекты больше нет ссылок. Сборщик мусора обычно освобождает неиспользуемую память в более поздний момент времени. Следовательно, ваш код в UC_Unloaded не сразу освободит память. Для теста вызовите сборщик мусора вручную, чтобы проверить, действительно ли произошла утечка памяти.

2. с помощью GC. Collect() не обнаружил никаких изменений в освобождении памяти.

3. Для тестирования вам следует использовать GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); ; в противном случае финализаторы вызываться не будут. Если это не изменяет потребление памяти: какой тип данных объектов .NET является утечкой? (Вы можете определить это с помощью инструмента профилирования памяти или с помощью WinDbg).

4. @fmunkert проблема была решена с помощью GC. Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); Спасибо. но я не понимаю, почему требуется вызывать GC. Собирать принудительно.

5. Боюсь, вы меня неправильно поняли. Вызов GC предназначен только для тестирования; вы никогда не должны этого делать в релизной версии вашей программы. Если утечка памяти исчезает при вызове GC, то это означает, что утечки памяти вообще не было. Помните об этом. NET задерживает освобождение памяти до тех пор, пока это не станет абсолютно необходимым; поэтому программы на .NET часто потребляют больше памяти, чем сопоставимые собственные программы. Но как только операционной системе потребуется больше памяти, .NET GC обеспечит освобождение неиспользуемой памяти.