WPF内存泄漏

时间:2011-07-01 12:05:46

标签: c# wpf xaml memory memory-leaks

我有一个简单的wpf应用程序。在主窗口中,我有堆栈面板和2个按钮。第一个按钮添加了100个用户控件(没有任何数据绑定,事件,位图),第二个按钮从面板中删除所有这些控件并调用GC.Collect()。还有一些问题: 1.在我第一次单击“删除”按钮后,并非所有内存都被释放,我必须点击几次才能释放更多内存。 2. 5-10分钟内存释放后,但不会有几兆字节。

例如我的应用程序启动后需要~22mb 当我添加500个控件 - 约60mb 我第一次点击“删除”按钮后 - ~55mb(我等了一段时间,内存没有被释放) 我点击几次,内存下降到25mb, 我不明白这一点,我是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 个答案:

答案 0 :(得分:11)

  

我想立即释放记忆。

不要。信任GC。

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

不要。信任GC。

  

5-10分钟后释放内存

我不是说信任GC吗?


  • 垃圾收集模型将确保释放系统中不需要的托管内存(其中包括几乎所有控件内存)。它使用一种优化算法,包括代,可用的可用内存,可能的CPU可用......所以GC.Collect()会干扰它。

  • GC.Collect()是异步的,因此没有立即生效。

  • 您需要注意的唯一资源是非托管资源,通常由 Dispose Pattern 处理。否则不要乱用GC,它的工作做得很好。

答案 1 :(得分:4)

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

这是一种将非GCable对象过早地强制转换为Gen2的可靠方法,因此可以在更长的时间内增加内存占用,这是没有充分理由的。

正如Aliostad所说:不要!

答案 2 :(得分:3)

单独留下垃圾收集器并让它完成其工作。

您所描述的不是内存泄漏。它是动态内存,在您认为它应该被释放的那一刻不会被释放。

你是垃圾收集者吗?你不是。担心什么时候收集垃圾不是你的工作。如果这些对象实际上是垃圾 - 而且它们是 - 当你需要它时,内存就会存在。

答案 3 :(得分:1)

在垃圾收集环境中,立即释放内存 并没有多大意义。

由于CLR JIT按需编码,因此第一次运行测试时,您不应该看到内存回退到最初的位置。这是有道理的,因为已经遵循新的代码路径并且代码已被JIT。那个代码需要驻留在内存中的某个地方吗?

因此,在第一次测试运行后,您应该无法收回到初始内存占用量。您的基线应该是运行测试一次后获得的内存使用量,而不是之前的内存使用量。在第二次运行之后,我可以通过多个集合将内存恢复到基线。

另外,我建议在发布模式下运行项目,不附加调试器。使用附加的调试器运行程序不会显示真正的内存配置文件,因为它使用各种技巧来保持对象(例如Collect objects still in scope - GC.Collect)。

然而,这完全是一个有争议的问题,因为正如我上面所述,在GC环境中(在大多数情况下)回收内存并不是很有意义。

答案 4 :(得分:0)

我会同意@Aliostad re GC。我做得很好,但它不是一个神奇地清除你所有记忆的工具。

如果您有内存泄漏问题,最直接和最可靠的解决方案是使用分析器,它应该能够识别您是否有真正的泄漏以及它在哪里。我使用过红门的蚂蚁,但其他人可能会有更好的建议。

遵循通常的指导方针,例如确保妥善处理物品。调用GC并希望它能够正常工作不是正确代码评估的替代方案。

答案 5 :(得分:0)

通过使用这个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);
}
}