When using a child window control, memory used by the control does not get released or freed when the control closes. Here are the steps to reproduce the issue.
- 1. Create a new Silverlight project.
- 2. Add Child Window Control.
- 3. Add any large png file. It helps with memory tracking later on.
- 4. In MainPage.xaml:
a. Add a button:
<Button Content="Click" Click="Button_Click" Width="100" Height="50"></Button>
- 5. In MainPage.xaml.cs:
a. // The Child Window
ChildWindow1 mywin;
void Button_Click(object sender, RoutedEventArgse)
{
mywin = new ChildWindow1();// hook closed event the release mywin object 、
mywin.Closed += new EventHandler(win_Closed);
mywin.Show();
}
void win_Closed(object sender, EventArgs e)
{// unhook event and release mywin object
mywin.Closed -= win_Closed;
mywin = null;
}
- 6. In ChildWindow1.xaml
a. Add Image to help track memory usage
<Grid.Background>
<ImageBrush ImageSource="Images/background1.png" Stretch="UniformToFill" />
</Grid.Background>
- 7. Run the application.
- 8. Open Task Manager and locate the process that is hosting the application: iexplorer.exe. Make a note of the current memory usage, ie 25876k
- 9. Click the button in the app to open the child window. Again note the memory usage, ie. 33400k
- 10. Close the child window. The memory usage never goes back to the initial 25876k
This is quite a big issue for us, since we have create an application that uses numerous child windows. After 4 hours of usage iexplorer.exe uses over 1.5 GB memory.
Can this be resolved or is there any work around.
上面的一段话是我在国外论坛上看到的一个帖子,非常的典型,Silverlight有很多类似的问题的,UI对象创建后,用完了,销毁掉,内存就是没办法释放掉,微软好像也无能为力的样子。其实这些问题都有一个共同特点:对象的引用明明销毁掉,但是内存就是回收不了,其实这个时候往往有一个你看不见也想不到指针或者事件指向它,这个指针往往在控件的使用上是必须的。基于以上的一个共同特点,高手都推荐用 ”弱引用” 来解决此类问题。”弱引用“ 是对象介于 可回收状态和强引用状态之间的一个状态,有兴趣的人可以去看Jeffrey Richer写的一本叫《.net 框架程序设计》,里面对垃圾回收器的回收算法,代龄,强引用,弱引用,对象池,以及非托管资源的释放,都有很详细的描述。“弱引用”方法原理很简单,重写微软的控件,把那个看不见,想不到的强引用指针改弱引用,这样时间久了,垃圾回收器会去回收对象的资源,简单地讲,"弱引用"是一种可以回收的,可以不回收的状态。原理很简单,但是做起来确很复杂,很多时候都不知道从那里开始,我中间尝试过几次都没有成功,看到这里请记下来一个很重要的专业名词“弱引用”。
上面是讲一些原理东西,下面讲一下实际项目的应用
项目中用到一个Silverlight 的 TreeView控件,对于小数据量的东西,Silverlight 的TreeView控件做得还是很不错的,该有的功能都有,简单好用,功能强大。但是对大数据量东西,就会出现瓶颈。我实际项目中有1万多辆车加上一些区域,每个区域,每个车辆都是树形目录上的一个节点,也就是我将近有2万个节点,这两万个结点数据绑上去还不错,可是一张开子节点,并且子节点稍微多一点,这个时候内存会涨上百M。后面我想了想用异步的方式加载,收缩的时候就把节点从界面上清除掉,这样可以减少内存,等异步的方式出来后我发现内存回收不干净,总有一些残余,而且量还是比较大的,即使我疯狂地使用 Jeffrey Richer 推荐的强制回收的方法:
GC.Collect();
GC.WaitForPending();
GC.Collect();
我点了N次,这三行代码执行了N次,鼠标都快被我点坏了,还是回收的不干净,干!于是乎我绝望,我再一次想到了”弱引用“,很遗憾”弱引用“比较难实现,而且效果不一定行,因为可能有多个指针指到这个内存块,那些指针都不知道,弱引用是没办法用的。
想到用Listbox来模拟实现TreeView ,应该来说是非常偶然的。一次非常偶然的机会,我使用silverlight 的Listbox,我给Listbox增加5000个节点我发现内存没怎么涨,就涨了不到10M,我在增加1万个节点,发现内存居然不涨,于是乎我就很自然的想到用Listbox模拟TreeView,而且内存就是涨了10M,能不能回收根本就无所谓 。至于怎么模拟,那是很简单的事情,纯粹就是体力活,有空把代码贴出来。