2011-07-01 74 views
6

我有一个简单的WPF应用程序。在主窗口中,我有堆栈面板和2个按钮。第一个按钮添加100个我的用户控件(没有任何数据绑定,事件,位图),然后从面板中删除所有这些控件并调用GC.Collect()。并且存在一些问题: 1.在我第一次单击“删除”按钮后,并非我所有的内存版本都已发布,并且我必须多次单击它以释放更多内存。 2.经过5 - 10分钟的内存释放,但几兆字节不。WPF内存泄漏

例如我的应用程序启动后,大约需要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(); 
     } 
+1

向我们展示一些代码,好不好? – Aliostad

+0

你如何衡量“内存使用量”?在Windows任务管理器? – spender

+1

@Alistad意味着什么:您能提供一个能够重现问题的*最小*工作示例的代码吗? – Heinzi

回答

11

我要立即释放内存。

不要。信任GC。

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

不要。信任GC。

后5 - 10分钟的内存释放

我不是说过信任GC?


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

  • GC.Collect()是异步的,所以不会立即生效。

  • 您需要注意的唯一资源是通常由处置的非托管资源处置模式。否则,不要搞乱GC,它的工作非常好。

+0

但我没有使用非托管资源(如果我不认为非托管资源是本地资源处理程序,文件描述符等)。如果GC没有帮助我可以使用。在我们的应用程序中,这个控件会被多次添加和删除,如果这是内存泄漏,我有问题,一段时间后应用程序会占用太多的内存并摔倒( –

+0

是的,这就是为什么你不需要担心它我只是为了完整而提到它 – Aliostad

+0

“不用担心”,虽然在垃圾收集环境中通常都是如此,但在这里感觉有点像手工波浪,如果你有一个漏窗(并且有很多方法可以发生,例如不注销一个订阅的事件),方法andronz张贴是一种有效的,尽管哈克,检测这种方式。我的假设是,他张贴的代码纯粹是一种测试性质 – DavidN

0

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

如果你有内存泄漏问题,最直接和最可靠的解决方案是使用一个探查器,它应该能够识别你是否有真正的泄漏和它在哪里。我已经使用红门蚂蚁,但其他人可能有更好的建议。

以及遵循通常的指导方针,如确保您妥善处理的东西。调用GC并希望它能起作用不是正确的代码评估的替代选择。

1

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

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

因此,在第一次测试运行后,您应该无法收回到您的初始内存占用空间。你的基准应该是你在运行一次测试后得到的内存使用量,而不是之前。在第二次运行后,我可以通过一些集合将内存还原到基线。

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

然而,这是一个有争议的问题,因为就像我上面所说的那样,在GC环境中(大多数情况下)立即回收内存没有多大意义。

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

这是迫使非GCable物体进入第二代提前,从而增加你的内存占用的时间较长,毫无理由的好办法。

正如Aliostad所言:不!

3

让垃圾收集器独立,让它做好工作。

你所描述的不是内存泄漏。这是动态内存在你认为它应该被释放的时刻没有得到释放。

你是垃圾收集器吗?你不是。担心垃圾被收集的时间不是你的工作。如果这些对象实际上是垃圾 - 它们是 - 当你需要它时,内存就会在那里。

+0

“你是垃圾收集器?你不是......“我喜欢这个,并且会使用它:D – Andy

0

通过使用这个DLL调用我们可以realocate内存资源

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); 
} 
}