首先,Dispose()
不保证会释放内存(它不标记对象进行 GC 收集,以防MemoryStream
- 它不释放任何内容,因为MemoryStream
没有非托管资源)。释放所使用的内存的唯一可靠方法MemoryStream
是丢失对它的所有引用并等待垃圾收集发生(如果你有OutOfMemoryException
- 垃圾收集器已经尝试但未能释放足够的内存)。此外,分配如此大的对象(任何 > 85000 字节)会产生一些后果 - 这些对象将进入大对象堆 (LOH),这可能会变得碎片化(并且无法压缩)。由于 .NET 对象必须占用连续的字节序列,这可能会导致您有足够的内存,但没有空间容纳大对象。在这种情况下,垃圾收集器无济于事。
似乎这里的主要问题是对stream
对象的引用保留在堆栈上,从而阻止了stream
对象的垃圾收集(即使强制垃圾收集也无济于事,因为 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(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);
}
}
}