C#发送文件时占用大量内存,系统.内存不足异常



我正在使用一个程序,该程序使用TCP通过网络发送/接收文件。该程序发送多个文件,因此在用户退出该程序之前,流不会关闭。

我面临的问题是,当我发送一个700mb的文件时,我的服务器程序专用内存会增长到700000K,严重削弱了我的计算机性能。当试图发送另一个700mb的文件时,服务器抛出一个System.OutOfMemoryException

有人能告诉我我做错了什么吗?

服务器端代码:

using (   FileStream fs = new FileStream("dracula.avi", FileMode.Open, FileAccess.Read))                            
{                                                        
byte[] data = new byte[fs.Length];
int remaining = data.Length;
int offset = 0;
strWriter.WriteLine("Content-Length: " + data.Length);
strWriter.Flush();
Thread.Sleep(1000);
while (remaining > 0)
{
Thread.Sleep(10);
int read = fs.Read(data, offset, remaining);
remaining -= read;
offset += read;
}
fs.Flush();
fs.Close();
}   
strm.Write(data, 0, data.Length);
strm.Flush();
GC.Collect();

您当前正在将整个文件读取到内存中,即使您只想将其复制到另一个流中。不要那样做。只需一次迭代一个区块:读取区块、写入区块、读取区块、编写区块等等。如果你正在使用。NET 4,您可以使用Stream.CopyTo来实现此目的。

您缓冲的是读取,而不是写入。这个程序正是按照你的要求去做的——分配一个巨大的区块或内存,并在发送一个字节之前将其全部填满。

更好的方法是从文件中读取一个小块(为了便于论证,4096字节),然后将该块写入输出流。通过这样做,每个连接将只使用4096个字节,这更具可扩展性。

OOM情况通常发生在系统内存不足或在32位进程中地址空间(2000MB)不足的情况下。

你说它可以成功复制一个,但不能复制两个?这两个是同时发生的还是连续发生的?你的线程模型是什么?另外,这个例子是一个片段,你似乎有一个StreamWriter和一个Stream用于写作,这些对象会消失吗?

小心GC。收集Microsoft不建议显式调用,因为如果您不正确使用它,可能会导致对象的生存时间超过所需时间。这是因为当你做GC时。收集,您正在将对象提升到更高一代。根据我的经验,最好确保您正在释放对象,并让框架决定GC的内容/时间。收集

我会熟悉WinDBG+SOS,这允许您查看堆上的对象。

试试这个:

  1. 启动WinDBG并连接到您的进程
  2. 如果使用4.0,请键入".loadby sos clr",否则键入".loadsby sos mscorwks">
  3. 按F5继续
  4. 复制一个文件,等待它完成
  5. 按CTRL+BREAK
  6. 键入"!dumpeap-stat",查看结果,查找应该消失的对象
  7. 对于每个应该消失的对象,获取MT值
  8. 键入"!dumpeap-mt{0}",将{0}替换为上面步骤中的值
  9. 这是一个实例列表,抓取其中一个对象地址
  10. 键入"!gcroot{0}",将{0}替换为对象地址

这应该会告诉你什么是对象的根,然后你需要找出如何展开,例如不需要的null对象。

最好在读取数据块后立即发送。我没有测试代码,但它应该类似于;

var bufferLenght = 1024;
byte[] buffer;
while (remaining > 0)
{
buffer = new byte[1024];
int len = fs.Read(buffer, offset, bufferLenght);
remaining -= len;
offset += len;    
strm.Write(buffer, 0, len);
}

相关内容

  • 没有找到相关文章

最新更新