l 内存泄露根源:
n .net垃圾回收器(GC)是通过判断某个对象是否存在对于此对象的强引用,如果存在强引用则不会被回收
n 举例:
object objA = new object(); object objB = objA; objA = null; // 强迫回收。 GC.Collect(); objB.ToString(); |
n objA引用的对象并没有被回收,因为这个对象还有另一个引用,ObjB。
n 解决方法:
u 断开对象的强引用
u 使用弱引用替代强引用
参考文献:
http://blog.csdn.net/sykpboy/archive/2005/04/11/342971.aspx
http://www.cnblogs.com/bayonetxxx/archive/2009/06/02/1494728.html
l 泄露实例:
1. 时间句柄使用
a) Code:
Window1.w1.TextBox1.TextChanged += new TextChangedEventHandler(TextBox1_TextChanged); |
b) Fix:
Window1.w1.TextBox1.TextChanged -= new TextChangedEventHandler(TextBox1_TextChanged); |
c) Ps:在windows1存在对当前窗口的强引用,使得在windows1生命周期内当前窗口无法回收。
2. 数据绑定使用
a) Code:
myDataBinding = new Binding("Children.Count"); myDataBinding.Source = myGrid; myDataBinding.Mode = BindingMode.OneWay; MyTextBlock.SetBinding(TextBlock.TextProperty, myDataBinding); 或是 <TextBlock Name="MyTextBlock" Text="{Binding ElementName=myGrid, Path=Children.Count}" />
|
b) Fix:
BindingOperations.ClearBinding(MyTextBlock, TextBlock.TextProperty); |
c) Ps:这种内存泄露只发生在Path属性不是一个依赖属性并且不是一个继承自InotifyPropertyChanged的类并且还要有一个强引用存在。
d) 微软描述:http://support.microsoft.com/kb/938416/en-us/
3. 命令绑定使用
a) Code:
command = new RoutedCommand("ClearBox", this.GetType()); command.InputGestures.Add(new KeyGesture(Key.F5)); myCmdBinding = new CommandBinding(command, F5CommandExecute); Window1.w1.CommandBindings.Add(myCmdBinding); //add binding to Window 1
|
b) Fix:
Window1.w1.CommandBindings.Remove(myCmdBinding); |
c) Ps:命令绑定是一种强引用。使得windows1中的对象在windows1回收前无法被回收。
4. 静态事件句柄使用
a) Code:
Application.Current.Activated += new EventHandler(App_Activated); |
b) Fix:
Application.Current.Activated -= new EventHandler(App_Activated); |
c) Ps:当前窗口存在一个关于静态事件的引用所以不能被释放
5. BitmapImage被设置为Image Source
a) Code:
static BitmapImage bi1; bi1 = new BitmapImage(new Uri("Bitmap1.bmp",UriKind.RelativeOrAbsolute)); //bi1 is static //bi1.Freeze() //if you do not Freeze, your app will leak memory m_Image1 = new Image(); m_Image1.Source = bi1; MyStackPanel.Children.Add(m_Image1);
|
b) Fix:
bi1.Freeze();
c) Ps:WPF保持一个强引用在静态BitmapImage和Image之间。
6. BitmapImage被设置为Image Source(多引用)
a) Code:
static BitmapImage bi1 = new BitmapImage(new Uri("Bitmap1.bmp", UriKind.RelativeOrAbsolute)); static BitmapImage bi2 = new BitmapImage(new Uri("Bitmap2.bmp", UriKind.RelativeOrAbsolute)); if (bi2.CanFreeze) bi2.Freeze(); //bi1.Freeze() //even though you are really using bi2 for Image Source, you also need to Freeze bi1 it to avoid leak m_Image1 = new Image(); m_Image1.Source = bi1; // use un-frozen bitmap, which causes the leak m_Image1.Source = bi2; // use frozen bitmap MyStackPanel.Children.Add(m_Image1);
|
b) Fix:
bi1.Freeze();
c) Ps:当Image获得一个新的source (e.g. m_Image1.Source = bi2;),WPF不会去移出老被勾住的bi1对象。
7. 下载BitmapImage在Image Source中
a) Code:
BitmapImage image = new BitmapImage(); image.BeginInit(); image.UriSource = new Uri(@http://www.somesite.com/some_image.png, UriKind.RelativeOrAbsolute); image.CacheOption = BitmapCacheOption.OnLoad; image.CreateOptions = BitmapCreateOptions.None; image.EndInit();
m_Image1 = new Image(); m_Image1.Source = image; MyStackPanel.Children.Add(m_Image1);
|
b) Fix: 将所需下载的BitmapImage先下载到一个临时文件夹中然后在使用本地的BitmapImage。
c) Ps:WPF不会移出一个被用于网络下载的特定对象的内部引用。这种内存泄露只发生于从Internet上下载图片。
8. 初始HWND在XP上被撤销就会导致CMilChannel泄漏
a) Fix: 在创建任何其他的HWND之前先创建一个新的HwndSource。这样WPF将使用这个HwndSource去发送从渲染线程到UI线程的消息。这就保证所有的消息被处理。
b) Ps:这种内存泄露的发生是因为WPF使用HWND去发送从渲染线程到UI线程的消息。当销毁第一个HWND并且在新窗口中开始一个动画,这就会引起从渲染线程到UI线程的消息堆积而没有被处理,泄露内存。
9. 使用绑定的每条线程会导致ShutdownListener泄漏
a) Fix: 无法解决
b) Ps:这种内存泄露的发生是因为在每个使用了绑定的线程中,使用了WPF的数据绑定的事件句柄被挂钩了但从不会被解挂钩。例如建立很多新的线程并且每个线程都创建一个使用数据绑定的新窗口。
10. 使用绑定的每条线程会导致ShutdownListener泄漏
a) Fix: 无法解决
b) Ps:这种内存泄露的发生是因为在每个使用了绑定的线程中,使用了WPF的数据绑定的事件句柄被挂钩了但从不会被解挂钩。例如建立很多新的线程并且每个线程都创建一个使用数据绑定的新窗口。
11. 在XP的硬件渲染过程中创建和销毁WriteableBitmap
a) Fix: 设置HwndTarget.RenderMode = RenderMode.SoftwareOnly强制使用软件渲染
12. Viewport3D w/ VisualBrush, WriteableBitmap, etc, leaks in Windows XP in SW
a) Fix:
i. 如果可以,使用硬件渲染的方法。
ii. 如果硬件渲染不行,使用SolidColorBrush来替换。
b) Ps:当VisualBrush, WriteableBitmap或是一些可选的其他类在Viewport3D中使用并且处于软件渲染的模式下。
参考文献:http://blogs.msdn.com/jgoldb/archive/2008/02/04/finding-memory-leaks-in-wpf-based-applications.aspx
l .net GC工作原理:
http://www.cnblogs.com/RicCC/archive/2009/09/01/dotnet-memory-management-and-garbage-collection.html