“实践是检验真理的唯一标准”,再多的理论也不如我们的测试。下面我就用代码来测试关于内存问题我们应该如何处理。
新建一个项目,只要有一个窗体就可以了,再加一个按钮,代码如下,
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
for(int i=0;i<10;i++)
{
Form form = new Form();
form.Show();
}
}
}
为了让测试更精确,我不在调试下运行程序,而是直接运行bin文件夹下的程序,我们看下面的几个图对比一下:
第三个图让人有点不可理解,为什么变小了。打开10个空白窗体,好像也不费什么内存,我们给打开的窗体加一个背景看看会怎么样。加载的背景在下面,及测试结果。
代码只是改了一下
for(int i=0;i<10;i++)
{
Form form = new Form();
form.BackgroundImage = Properties.Resources.formbackgroup;
form.Show();
}
显然,窗体关闭后,内存也没有被放出来,很多的人可能都知道,NET写出来的程序只要开着不动,隔一天来之后,它都会变得慢。不能释放出来的内存,我们都知道是那个资源文件搞得,既然我们它不能放出来,那我们就要想办法让它放出来,或者想办法,当打开的时候,它不占这么多的内存,这才是正道,办法还是有的,我们只要再改一下代码,立马就会看到效果了。我第一次测试这个问题的时候我就感觉到是资源文件在作怪,我就直接定义了一image的属性,然后把这个属性给打开的窗体,下面是代码和测试结果,估计也是很多人想不到的:
private Image _bgImage = null;
public Image BgImage
{
get
{
if (_bgImage== null)
{
_bgImage = Properties.Resources.formbackgroup;
}
return _bgImage;
}
}
private void button1_Click(object sender, EventArgs e)
{
for(int i=0;i<10;i++)
{
Form form = new Form();
form.BackgroundImage = BgImage;
form.Show();
}
}
很明显,同样是同一张图片,可是差了2M的虚拟内存,以下是我的估计,(没有理论根据),当程序加载我们程序中的资源文件时,是每加载一次,就当一个新文件加到内存中,窗体开10个,那就加了10个图片到内存中当,当我定义属性的时候,大家应该知道,图片是一个文件,文件具有唯一性,那么无论你将它给了谁,都是同一个,它也只加载一次,因为我的属性保存证了它只加载一次,这就有点像ado.net中的datatale一样,它也是文件,无论你怎么传,都不用加 ref,可是为什么资源文件不是这样呢,我没有想过资源文件内部是如何读到内存中的。所以当我们以后用到资源文件时,如果这个图片用到很多地方,那这个问题,你不得不考虑一下了。结果测出来明显有差别了,那是否还有更好的办法来处理呢,就比如你就是想直接加载资源文件到控件中,那么你一得要记得,你一定要写代码来将这个背景销毁了,很明显,MS没有帮我们销毁,代码很简单,如下就可以达到了
private void form_FormClosed(object sender, FormClosedEventArgs e)
{
Form f = (Form)sender;
if (f.BackgroundImage != null)
{
f.BackgroundImage.Dispose();
f.BackgroundImage = null;
}
}
大家可能听说过windows003里面有一个叫做emptry的工具,它其实也没什么用,它只是调用了一个API,
[DllImport("kernel32.dll")]
public static extern bool SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);
public static void GarbageCollect()
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
public static void FlushMemory()
{
GarbageCollect();
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
}
}
它的作用和上面的代码的效果是一样的,至少我测试发现是一样的。物理内存减下来,说白了,和没减是一样的,关键是虚拟内存才是问题的所在。不过适时的调用一下上面的还代码,还是会起到微小的作用的,怎么说有总比没有强。写代码时,遇到消耗内存的时候,只要我们多测测,多想想,还是能省点内存的。看似普通的代码(比如上面的资源文件),不测试也不会发现问题的所在。