2011-07-01 73 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); 
} 
}