WPF memory leak

I have a simple wpf application. In the main window, I have a stack panel and two buttons. The first button adds 100 of my user controls (without binding data, events, bitmaps), and the second removes all of them from the panel and calls GC.Collect (). And there are some problems: 1. After I first clicked the β€œDelete” button, not all of my memory releases, and I have to click it several times to free up more memory. 2. After a 5-10 minute memory release, but not less than megabytes.

for example, after starting my application, ~ 22 MB is required when I add 500 controls - ~ 60 MB after I clicked the delete button for the first time - ~ 55mb (I wait a while, the memory is not freed) I click several times , and the memory dropped to 25 MB, I do not understand this, I am new to WPF, and maybe I will miss something I want to free up memory immediately.

<Window x:Class="WpfApplication10.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="385" Width="553"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="25" /> <RowDefinition Height="240*" /> <RowDefinition Height="25" /> </Grid.RowDefinitions> <Grid Name="border1" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" > <ScrollViewer VerticalAlignment="Stretch" Name="scrollViewer1" HorizontalAlignment="Stretch"> <StackPanel Margin="3,3,3,3" Background="Transparent" VerticalAlignment="Stretch" Name="activityStackPanel" HorizontalAlignment="Stretch"> </StackPanel> </ScrollViewer> </Grid> <Button Content="Button" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="12,0,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" /> <Button Content="Button" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="141,0,0,0" Name="button2" VerticalAlignment="Top" Width="75" Click="button2_Click" /> <Label Content="Label" Grid.RowSpan="2" Height="28" HorizontalAlignment="Left" Margin="34,0,0,0" Name="label1" VerticalAlignment="Top" /> </Grid> 

 namespace WpfApplication10 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void button1_Click(object sender, RoutedEventArgs e) { int N = 100; //var r = new ActivityStatisticItem("111", "222", DateTime.Now, "333", 1); for (int i = 0; i < N; i++) { activityStackPanel.Children.Add(new UserControl1()); } label1.Content = activityStackPanel.Children.Count; } private void button2_Click(object sender, RoutedEventArgs e) { activityStackPanel.Children.Clear(); label1.Content = activityStackPanel.Children.Count; GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); } } } <UserControl x:Class="WpfApplication10.UserControl1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Background="Transparent" Margin="0,2,0,2" MinHeight="80" MinWidth="130" MaxHeight="80"> <Grid Width="441"> <Grid.RowDefinitions> <RowDefinition Height="40" Name="rowTop" /> <RowDefinition Height="40" Name="rowBottom"/> </Grid.RowDefinitions> <Border BorderBrush="Gray" BorderThickness="1" HorizontalAlignment="Stretch" Background="LightGreen" Name="contactPanel" CornerRadius="3,3,3,3" VerticalAlignment="Stretch" Panel.ZIndex="1" > <Grid VerticalAlignment="Stretch" Name="grid1" Margin="3,0,3,0" HorizontalAlignment="Stretch"> <Label Content="Contact" Height="15" HorizontalAlignment="Left" Margin="15,3,0,0" Name="headerLabel" Padding="0" VerticalAlignment="Top" FontSize="10" FontWeight="DemiBold"/> <Label Content="00/00/0000 00:00:00" Height="15" HorizontalAlignment="Left" Margin="13,18,0,0" Name="timeLabel" Padding="0" VerticalAlignment="Top" FontSize="10" Width="100" FontWeight="DemiBold" /> <Label Content="00:00:00" Height="15" HorizontalAlignment="Right" Margin="0,18,0,0" Name="durationLabel" Padding="0" VerticalAlignment="Top" FontSize="10" Width="38" FontWeight="DemiBold"/> <!--<Image Height="12" HorizontalAlignment="Left" Margin="0,3,0,0" Name="directionPictureBox" Stretch="Fill" VerticalAlignment="Top" Width="12" /> <Image Height="12" HorizontalAlignment="Right" Margin="0,20,41,0" Name="timerImage" Stretch="Fill" VerticalAlignment="Top" Width="12" /> <Image Height="12" HorizontalAlignment="Left" Margin="0,20,0,0" Name="dateTimeImage" Stretch="Fill" VerticalAlignment="Top" Width="12" />--> </Grid> </Border> <Border BorderBrush="Gray" BorderThickness="1,0,1,1" Grid.Row="1" Background="White" HorizontalAlignment="Stretch" Margin="10,0,10,0" Name="detailsPanel" CornerRadius="0,0,3,3" VerticalAlignment="Stretch"> <Grid HorizontalAlignment="Stretch" Name="grid2" Margin="3,0,3,0" VerticalAlignment="Stretch"> <Label Content="Label" Height="15" HorizontalAlignment="Stretch" FontSize="9" Padding="0" Margin="0,3,0,0" Name="numberRadLabel" VerticalAlignment="Top" /> <Label Content="Label" Height="15" HorizontalAlignment="Stretch" FontSize="9" Padding="0" Margin="0,18,0,0" Name="queueRadLabel" VerticalAlignment="Top" /> </Grid> </Border> </Grid> 

In user management, I only have

  public UserControl1() { InitializeComponent(); } 
+6
source share
6 answers

I want to free memory immediately.

not to do. Trust GC.

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

not to do. Trust GC.

After freeing up memory for 5-10 minutes

Aren't I talking about GC trust?


  • The garbage collection model will ensure that freed managed memory is released on your system (including almost all of your management memory). It uses an optimization algorithm that includes generations, free available memory, possibly an available CPU ... so GC.Collect() will interfere with it.

  • GC.Collect() is asynchronous, therefore an immediate effect.

  • The only resource you need to be careful of is the unmanaged resource, which is usually handled by the Dispose Pattern . Otherwise, do not spoil the GC, it does its job very well.

+11
source
 GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); 

This is a surefire way to prematurely force non-GCable-related objects into Gen2, thereby increasing memory over a longer period of time, with no good reason.

As Aliostad said: no!

+4
source

Leave the garbage collector alone and let it do its job.

What you are describing is not a memory leak. This dynamic memory is not freed when you think you need to free it.

Are you a garbage collector? You do not. Worrying about when to pick up trash is not your job. If these objects are actually garbage - and they are - the memory will be there when you need it.

+3
source

In a garbage collection environment, freeing up memory doesn't make sense right away.

Since the CLR JIT code is on request, when you first run the test, you should not see a memory drop back to where it was originally. This makes sense because the new code codes were followed and the code was JITted. This code should be somewhere in memory not?

Therefore, after the first test run, you will not be able to get back your original amount of memory. Your baseline should be the memory that you receive after running the test once, and not earlier. After the second run, I can return the memory to the baseline with several collections.

In addition, I would recommend starting the project in release mode without a debugger prefix. Running your program with an attached debugger will not show you the true memory profile, since there are various tricks that it uses to store objects (for example, Collect objects still in the area - GC.Collect ).

This is all a moot point, however, because, as I said above, immediately reclamation of memory does not make much sense in the GC environment (in most cases).

+1
source

I agree with @Aliostad re GC. I do my job very well, BUT this is not a tool to magically clear all your memory.

If you have problems with a memory leak, the easiest and most reliable solution is to use a profiler, which should be able to determine if you have a real leak and where it is located. I used the Red Gate Ants, but others may have better offers.

As in accordance with the usual recommendations, for example, so that you dispose of the material correctly. Calling GC and hoping that it will work is not an alternative to correctly evaluating the code.

0
source

Using this Dll call, we can implement memory resources

 public class MemoryManagement { [DllImportAttribute("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)] private static extern int SetProcessWorkingSetSize(IntPtr process, int minimumWorkingSetSize, int maximumWorkingSetSize); public static void FlushMemory() { GC.Collect(); GC.WaitForPendingFinalizers(); if (Environment.OSVersion.Platform == PlatformID.Win32NT) { SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1); } } 
0
source

Source: https://habr.com/ru/post/891814/


All Articles