WPF内存泄漏
本文关键字:泄漏 内存 WPF | 更新日期: 2023-09-27 18:01:06
我有一个简单的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();
}
我想立即释放记忆。
不要。信任GC
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
不要。信任GC
5-10分钟后,内存释放
我不是说信任GC吗
垃圾回收模型将确保释放系统中不需要的托管内存(其中几乎包括所有控件内存(。它使用了一种优化算法,包括代、可用内存、可能的CPU。。。所以CCD_ 1会干扰它
GC.Collect()
是异步的,因此不会立即生效。唯一需要小心的资源是非托管资源,它通常由Dispose Pattern处理。否则,不要惹GC,它的工作做得很好。
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
这是一种毫无理由地提前将不可GCable对象强制进入Gen2的方法,从而在更长的时间内增加内存占用。
正如Aliostad所说:不要!
不要管垃圾收集器,让它来完成它的工作
你所描述的不是内存泄漏。这是一种动态记忆,在你认为应该释放的时候没有被释放。
你是垃圾收集器吗?你不是。担心垃圾何时被收集不是你的工作。如果这些对象实际上是垃圾——它们确实是——那么当你需要的时候,内存就会在那里
在垃圾回收环境中,立即释放内存实际上没有意义。
由于CLR JIT是按需编码的,所以在第一次运行测试时,您不应该看到内存回落到最初的位置。这是有意义的,因为已经遵循了新的代码路径,并且已经对代码进行了JITted。那个代码需要驻留在内存中的某个位置,不是吗?
因此,在第一次测试运行之后,您不应该能够收集回最初的内存占用。您的基线应该是在运行一次测试后获得的内存使用率,而不是之前。在第二次运行之后,我能够通过一些集合将内存恢复到基线。
此外,我建议您在没有附加调试器的发布模式下运行项目。运行带有调试器的程序不会向您显示真正的内存配置文件,因为它使用了各种技巧来保留对象(例如,Collect对象仍在作用域中-GC.Collect(
然而,这都是一个没有意义的问题,因为正如我上面所说,在GC环境中(在大多数情况下(立即回收内存没有多大意义。
我同意@Aliostad的GC观点。我做得很好,但它不是一个神奇地清除你所有记忆的工具。
如果你有内存泄漏问题,最简单可靠的解决方案是使用探查器,它应该能够识别你是否有真正的泄漏以及泄漏在哪里。我使用过红门的蚂蚁,但其他人可能有更好的建议。
除了遵循通常的指导方针,比如确保你正确处理了东西。调用GC并希望它能工作并不是正确代码评估的替代方案。
通过使用这个DllInvoke,我们可以重新定位内存资源
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);
}
}