C#内存管理

时间:2018-07-09 13:36:43

标签: c# wpf memory memory-management memory-leaks

我很想知道如何在c#应用程序中进行内存管理。

即使我处置对象并使其无效,我的应用程序也不会释放内存。 为了进行测试,我创建了以下示例应用程序。

一个应用程序有两个按钮 1)消耗内存 2)释放内存

通过单击消耗内存,我将创建500个内存流对象并将其添加到列表中。

通过单击Release Memory,我将处置所有Memory Stream对象,并使该List也无效。和收集垃圾。

但是,当我那时启动应用程序时,我的任务管理器将显示8.6 MB的内存使用量。那时我按“消耗内存”,任务管理器将显示679.6 MB的内存使用情况。那时我按Release Memory时,任务管理器将显示680.0 MB内存使用情况。

如何强制释放内存?

  

代码

public partial class MainWindow : Window
{
    List<System.IO.MemoryStream> MemoryStreamCollection = new List<System.IO.MemoryStream>();

    public MainWindow()
    {
        InitializeComponent();
    }

    private void ConsumeMemory_Click(object sender, RoutedEventArgs e)
    {
        for (int i = 0; i < 500; i++)
        {
            MemoryStreamCollection.Add(new System.IO.MemoryStream(System.IO.File.ReadAllBytes(@"D:\TestPDF.pdf")));
        }

        MessageBox.Show("Done");
    }

    private void ReleaseMemory_Click(object sender, RoutedEventArgs e)
    {
        foreach (System.IO.MemoryStream memoryStream in MemoryStreamCollection)
        {
            memoryStream.Dispose();
        }

        MemoryStreamCollection = null;

        GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);

        MessageBox.Show("Done");
    }
}
  

应用程序启动时的屏幕截图

enter image description here

  

点击消耗内存按钮后的屏幕截图。

enter image description here

  

单击释放内存按钮后的屏幕截图。

enter image description here

3 个答案:

答案 0 :(得分:1)

您为什么要尝试这样做?垃圾收集器会在感觉到垃圾时收集垃圾。尝试在C#中手动管理内存通常是一个坏主意。

也就是说,您可以尝试运行我不想被GC中断的基准测试时使用的以下一些小技巧(清理从上一个基准测试释放的所有内存)-

GC.Collect();  

GC.WaitForPendingFinalizers(); 

GC.Collect(); 

GC.WaitForPendingFinalizers();

答案 1 :(得分:0)

不建议显式调用gc,但如果您调用

System.GC.Collect();
System.GC.WaitForPendingFinalizers();

它将在整个代码中显式调用GC,请不要忘记调用GC.WaitForPendingFinalizers();。在GC.Collect()之后。

WaitForPendingFinalizers并不总是提供更好的性能,它只是阻塞,直到终结队列中的所有对象都终结为止。如果要收集这些对象,则需要再次调用System.GC.Collect。

答案 2 :(得分:0)

当您添加名为GarbageCollect的第三个按钮并按如下所示修改代码时,它将起作用:

using System;
using System.Collections.Generic;
using System.Windows;

namespace WpfApplication9
{
    public partial class MainWindow : Window
    {
        List<System.IO.MemoryStream> MemoryStreamCollection = new List<System.IO.MemoryStream>();

        public MainWindow()
        {
            InitializeComponent();
        }

        private void ConsumeMemory_Click(object sender, RoutedEventArgs e)
        {
            for (int i = 0; i < 500; i++)
            {
                MemoryStreamCollection.Add(new System.IO.MemoryStream(System.IO.File.ReadAllBytes(@"C:\temp\bpmn.png")));
            }

            MessageBox.Show("Done");
        }

        private void ReleaseMemory_Click(object sender, RoutedEventArgs e)
        {
            foreach (System.IO.MemoryStream memoryStream in MemoryStreamCollection)
            {
                memoryStream.Dispose();
            }

            MemoryStreamCollection = null;

            MessageBox.Show("Done");
        }

        private void GarbageCollect_Click(object sender, RoutedEventArgs e)
        {
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);

            MessageBox.Show("Done");
        }
    }
}

原因是在代码中,您正在调用GC.Collect,而foreach循环的临时变量仍在堆栈中:

private void ReleaseMemory_Click(object sender, RoutedEventArgs e)
{
    foreach (System.IO.MemoryStream memoryStream in MemoryStreamCollection)
    {
        memoryStream.Dispose();
    }

    MemoryStreamCollection = null;

    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);

    MessageBox.Show("Done");
}

通过将GC.Collect移出函数范围,List及其迭代器将不再位于本地堆栈上,GC可以成功完成其工作。

这是您的示例代码中MessageBox.Show("Done")方法中ReleaseMemory_Click()行之前的情况,如内存分析器中所示:

screen shot

如您所见,列表和一个MemoryStream由ReleaseMemory_Click()方法的堆栈帧保存。