Утечка памяти WPF
У меня есть простое приложение wpf. В главном окне у меня есть панель стека и 2 кнопки. Первая кнопка добавляет 100 пользовательских элементов управления my (без привязки данных, событий, растровых изображений), а вторая удаляет их все из панели и вызывает GC.Собирать(). И есть некоторые проблемы:
1. После того, как я нажал кнопку "Удалить" в первый раз не все мои релизы памяти, и я должен нажать его несколько раз, чтобы освободить больше памяти.
2. Через 5 - 10 мин память освобождается, но несколько мегабайт-нет.
Например, после моего приложения запускает он занимает ~22 Мб
когда я добавляю 500 элементов управления - ~60 Мб
после того, как я нажал кнопку" Удалить " в первый раз - ~55 мб (я жду некоторое время, память не освобождается)
я щелкнул несколько раз, и память упала до 25 Мб,
Я не понимаю этого, я новичок в WPF, и, возможно, я что-то упускаю
Я хочу немедленно освободить память.
<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>
В пользовательском управлении у меня есть только
public UserControl1()
{
InitializeComponent();
}
6 ответов:
Я хочу немедленно освободить память.Не доверяй GC.
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();Не доверяй GC.
Через 5-10 мин память освобождается
разве я не сказал "доверяй GC"?
Модель сборки мусора гарантирует, что ненужная управляемая память в вашей системе будет освобождена (которая включает почти всю вашу память элементов управления). Он использует алгоритм оптимизации, который включает в себя поколения, свободная память доступна, возможно, процессор доступен... так что
GC.Collect()будет мешать ему.
GC.Collect()является асинхронным, поэтому нет немедленного эффекта.Единственный ресурс, с которым нужно быть осторожным, - это неуправляемый ресурс, который обычно обрабатывается шаблономDispose . В противном случае не связывайтесь с GC, он делает свою работу очень хорошо.
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();Это верный способ преждевременно ввести в Gen2 не подлежащие GC-обработке объекты, тем самым увеличив объем памяти на более длительный период времени без веской причины.
Как сказал Алиостад: не надо!
оставьте сборщика мусора в покое, и пусть он делает свою работу.
То, что вы описываете, не является утечкой памяти. Это динамическая память, которая не освобождается в тот момент, когда вы думаете, что она должна быть освобождена.
Вы сборщик мусора? Вы не. Беспокоиться о том, когда мусор будет собран, - это не ваша работа. Если эти объекты на самом деле мусор - а они есть - память будет там, когда вам это нужно.
В среде, собранной из мусора, немедленное освобождение памяти на самом деле не имеет смысла.
Так как CLR JITs код по требованию, при первом запуске теста вы не должны видеть падение памяти туда, где она была изначально. Это имеет смысл, потому что новые пути кода были прослежены и код был JITted. Этот код должен находиться где-то в памяти, нет?
Таким образом, после первого тестового запуска вы не сможете вернуться к первоначальному набору данных. потребление памяти. Вашим базовым показателем должно быть использование памяти, которое вы получаете после выполнения теста один раз, а не раньше. После запуска во второй раз, я могу получить память обратно к базовой линии с рядом коллекций.
Кроме того, я бы рекомендовал запускать ваш проект в режиме выпуска без подключенного отладчика. Запуск программы с подключенным отладчиком не покажет вам истинный профиль памяти, так как существуют различные приемы, которые он использует, чтобы держать объекты вокруг (например, собирать объекты все еще в сфера применения-ГК.Соберите ).
Однако все это спорный вопрос, потому что, как я уже сказал выше, немедленное восстановление памяти не имеет большого смысла в среде GC (в большинстве случаев).
Я бы согласился с @Aliostad re GC. Я делаю свою работу очень хорошо, но это не инструмент, чтобы магически очистить всю вашу память.
Если у вас есть проблемы с утечкой памяти, самым простым и надежным решением является использование профилировщика, который должен быть в состоянии определить, есть ли у вас подлинная утечка и где она находится. Я использовал муравьев из Красных ворот, но у других могут быть лучшие предложения.
А также следовать обычным рекомендациям, например, убедиться, что вы правильно избавляетесь от вещей. Вызов GC и надежда на то, что он будет работать, не являются альтернативой для правильной оценки кода.
С помощью этого вызова Dll мы можем реалоцировать ресурсы памяти
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); } }
Comments