我很想知道如何在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");
}
}
应用程序启动时的屏幕截图
点击消耗内存按钮后的屏幕截图。
单击释放内存按钮后的屏幕截图。
答案 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()
行之前的情况,如内存分析器中所示:
如您所见,列表和一个MemoryStream由ReleaseMemory_Click()
方法的堆栈帧保存。