C# 如何强制释放MemoryStream占用的内存?

C# 如何强制释放MemoryStream占用的内存?,c#,.net,memory-management,garbage-collection,idisposable,C#,.net,Memory Management,Garbage Collection,Idisposable,我有以下代码: const int bufferSize = 1024 * 1024; var buffer = new byte[bufferSize]; for (int i = 0; i < 10; i++) { const int writesCount = 400; using (var stream = new MemoryStream(writesCount * bufferSize)) { for (int j = 0; j <

我有以下代码:

const int bufferSize = 1024 * 1024;
var buffer = new byte[bufferSize];
for (int i = 0; i < 10; i++)
{
    const int writesCount = 400;
    using (var stream = new MemoryStream(writesCount * bufferSize))
    {
        for (int j = 0; j < writesCount; j++)
        {
            stream.Write(buffer, 0, buffer.Length);
        }
        stream.Close();
    }
}
const int bufferSize=1024*1024;
var buffer=新字节[bufferSize];
对于(int i=0;i<10;i++)
{
常量int writesCount=400;
使用(var流=新的MemoryStream(writeCount*bufferSize))
{
对于(int j=0;j
我在32位机器上运行

第一次迭代完成得很好,然后在下一次迭代中,我在
new
s
MemoryStream
行上得到一个
System.OutOfMemoryException
异常


为什么以前的
MemoryStream
内存没有被回收,尽管
使用了
语句?如何强制释放MemoryStream使用的内存?

当您确定有必要清理未引用的对象时,尝试强制垃圾收集

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
另一种选择是将
与外部存储一起使用:
文件流
,例如

但是,在一般情况下,最好使用一个足够小的缓冲区(数组,分配一次)并将其用于读/写调用。避免在.NET中有许多大型对象(请参阅)

更新

假设
writescont
是常量,那么为什么不分配一个缓冲区并重用它呢

const int bufferSize = 1024 * 1024;
const int writesCount = 400;

byte[] streamBuffer = new byte[writesCount * bufferSize];
byte[] buffer = new byte[bufferSize];
for (int i = 0; i < 10; i++)
{
    using (var stream = new MemoryStream(streamBuffer))
    {
        for (int j = 0; j < writesCount; j++)
        {
            stream.Write(buffer, 0, buffer.Length);
        }
    }
}
const int bufferSize=1024*1024;
常量int writesCount=400;
byte[]streamBuffer=新字节[WriteCount*bufferSize];
字节[]缓冲区=新字节[bufferSize];
对于(int i=0;i<10;i++)
{
使用(var流=新内存流(streamBuffer))
{
对于(int j=0;j
看起来您分配的资源超出了系统的处理能力。您的代码在我的机器上运行良好,但如果我这样更改它:

const int bufferSize = 1024 * 1024 * 2;
我和你犯了同样的错误

但是如果我将目标处理器更改为x64,那么代码将运行,这似乎是合乎逻辑的,因为您可以处理更多的内存

关于本文的详细说明:
关于这个问题的一些信息:

我认为问题不在于垃圾收集器没有做好它的工作。如果GC在内存压力下,它应该运行并回收刚才分配的400 MB

这更可能是由于GC没有找到连续的400 MB块

相反,发生“内存不足”错误是因为进程无法运行 在其中查找足够大的连续未使用页部分 用于执行请求映射的虚拟地址空间

你应该读埃里克·利珀特的博客

你最好同时做下面的两件事

  • 重用已分配的内存块(为什么要创建另一个大小完全相同的内存块)
  • 分配更小的块(小于85KBs
  • 在dotnet4.5之前,Dotnet构建了两个堆,小对象堆(SOH)大对象堆(LOH)。见布兰登·布雷。您的
    MemoryStream
    正在LOH中分配,并且在整个过程中没有被压缩(碎片整理),这使得分配如此大量内存的多个调用更有可能引发
    OutOfMemoryException

    CLR管理两个不同的堆进行分配,即小对象 堆(SOH)和大对象堆(LOH)。有更大的分配吗 LOH上的字节数大于或等于85000字节。复制大型对象 具有性能惩罚,因此LOH不像SOH那样紧凑。 另一个定义特征是LOH仅被收集 在第2代收集期间。这些都有内置的功能 假设大型对象分配很少


    首先,
    Dispose()
    不保证释放内存(在
    MemoryStream
    的情况下,它不为GC收集标记对象-它不释放任何内容,因为
    MemoryStream
    没有非托管资源)。释放
    MemoryStream
    使用的内存的唯一可靠方法是丢失对它的所有引用,并等待垃圾回收发生(如果您有
    OutOfMemoryException
    ,垃圾回收器已尝试但未能释放足够的内存)。此外,分配这样大的对象(任何大于85000字节的对象)都会产生一些后果——这些对象将被放入大对象堆(LOH),这可能会导致碎片化(并且无法压缩)。由于.NET对象必须占用一个连续的字节序列,因此可能会导致这样的情况:您有足够的内存,但没有空间容纳大型对象。在这种情况下,垃圾收集器没有帮助

    这里的主要问题似乎是对
    对象的引用保留在堆栈上,从而阻止
    对象的垃圾收集(即使强制垃圾收集也没有帮助,因为GC认为该对象仍然是活动的,您可以检查这一点,为其创建一个
    WeakRefrence
    )。重构此示例可以解决此问题:

        static void Main(string[] args)
        {
            const int bufferSize = 1024 * 1024 * 2;
            var buffer = new byte[bufferSize];
            for(int i = 0; i < 10; i++)
            {
                const int writesCount = 400;
                Write(buffer, writesCount, bufferSize);
            }
        }
    
        static void Write(byte[] buffer, int writesCount, int bufferSize)
        {
            using(var stream = new MemoryStream(writesCount * bufferSize))
            {
                for(int j = 0; j < writesCount; j++)
                {
                    stream.Write(buffer, 0, buffer.Length);
                }
            }
        }
    
    static void Main(字符串[]args)
    {
    const int bufferSize=1024*1024*2;
    var buffer=新字节[bufferSize];
    对于(int i=0;i<10;i++)
    {
    常量int writesCount=400;
    写入(缓冲区、写计数、缓冲区大小);
    }
    }
    静态无效写入(字节[]缓冲区、整数写入计数、整数缓冲区大小)
    {
    使用(var流=新的MemoryStream(writeCount*bufferSize))
    {
    对于(int j=0;j
    下面是一个示例,它证明了对象不能被垃圾收集:

        static void Main(string[] args)
        {
            const int bufferSize = 1024 * 1024 * 2;
            var buffer = new byte[bufferSize];
            WeakReference wref = null;
            for(int i = 0; i < 10; i++)
            {
                if(wref != null)
                {
                    // force garbage collection
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    GC.Collect();
                    // check if object is still alive
                    Console.WriteLine(wref.IsAlive); // true
                }
                const int writesCount = 400;
                using(var stream = new MemoryStream(writesCount * bufferSize))
                {
                    for(int j = 0; j < writesCount; j++)
                    {
                        stream.Write(buffer, 0, buffer.Length);
                    }
                    // weak reference won't prevent garbage collection
                    wref = new WeakReference(stream);
                }
            }
        }
    
    static void Main(字符串[]args)
    {
    const int bufferSize=1024*1024*2;