Утечка памяти в WPF - PullRequest
6 голосов
/ 01 июля 2011

У меня есть простое приложение wpf.В главном окне у меня есть панель стека и 2 кнопки.Первая кнопка добавляет 100 моих пользовательских элементов управления (без каких-либо привязок данных, событий, растровых изображений), а вторая удаляет все из них с панели и вызывает GC.Collect ().И есть некоторые проблемы: 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 ]

11 голосов
/ 01 июля 2011

Я хочу немедленно освободить память.

Не. Доверие GC.

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

Не. Доверие GC.

Через 5 - 10 минут освобождения памяти

Разве я не сказал, доверять GC?


  • Модель сбора мусора обеспечит освобождение ненужной управляемой памяти в вашей системе (которая включает в себя почти всю вашу управляющую память). Он использует алгоритм оптимизации, который включает поколения, свободную память, возможно, доступный процессор ... так что GC.Collect() будет мешать ему.

  • GC.Collect() является асинхронным, поэтому немедленного эффекта нет.

  • Единственный ресурс, к которому нужно быть осторожным, - это неуправляемый ресурс, который обычно обрабатывается Dispose Pattern . В противном случае не связывайтесь с GC, он отлично справляется со своей работой.

4 голосов
/ 01 июля 2011
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

Это верный способ преждевременного форсирования объектов не-GCable в Gen2, что увеличивает объем памяти на более длительный период времени, без веской причины.

Как сказал Алиостад: don 'т!

3 голосов
/ 01 июля 2011

Оставьте сборщик мусора в покое и позвольте ему выполнять свою работу.

То, что вы описываете, не является утечкой памяти. Это динамическая память не освобождается в тот момент, когда вы думаете, что она должна быть освобождена.

Вы сборщик мусора? Ты не. Беспокоиться о том, когда мусор собирается, не твоя работа. Если эти объекты фактически являются мусором - и они есть - память будет там, когда вам это нужно.

1 голос
/ 01 июля 2011

В среде сборки мусора немедленное освобождение памяти не имеет смысла.

Поскольку код CLR JITs по требованию, при первом запуске теста не следуетувидеть падение памяти туда, где оно было изначально.Это имеет смысл, потому что новые пути кода были соблюдены, и код был JITted.Этот код должен находиться где-то в памяти нет?

Поэтому после первого пробного запуска вы не сможете восстановить исходный объем памяти.Базовым показателем должно быть использование памяти, которое вы получаете после однократного запуска теста, а не до.После второго запуска я смог вернуть память на базовый уровень с несколькими коллекциями.

Кроме того, я бы порекомендовал запустить ваш проект в режиме выпуска без отладчика.Запуск вашей программы с подключенным отладчиком не покажет вам истинный профиль памяти, так как есть различные приемы, которые он использует для удержания объектов (например, Сбор объектов, находящихся в области действия - GC.Collect ).

Однако это все спорный вопрос, поскольку, как я уже сказал выше, восстановление памяти сразу не имеет большого смысла в среде GC (в большинстве случаев).

0 голосов
/ 07 апреля 2015

Используя этот Dll Invoke, мы можем перераспределить ресурсы памяти

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 голосов
/ 01 июля 2011

Я бы согласился с @Aliostad re GC.Я очень хорошо справляюсь со своей задачей, НО это не инструмент для волшебной очистки всей вашей памяти.

Если у вас есть проблемы с утечкой памяти, самое простое и надежное решение - использовать профилировщик, который долженопределить, есть ли у вас настоящая утечка и где она находится.Я использовал Муравьев из Красных Ворот, но у других могут быть лучшие предложения.

А также следуя обычным правилам, таким как обеспечение правильной утилизации вещей.Вызов GC и надежда на то, что это сработает, не является альтернативой для правильной оценки кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...