一个大的System.IO.MemoryStream会导致我的应用程序的内存使用量急剧增加吗?

我正在构建一个允许用户从URL下载文件的库。 我正在考虑的一个选项是让用户为文件指定预期的MD5校验和; 库的GetFile(string url)函数确保下载的流的校验和与用户指定的校验和匹配。

知道HttpWebResponse.GetResponseStream()返回的NetworkStream是不可寻的,我找到了一种复制Stream的方法,这要归功于这个问题的答案: 如何在C#中读取两次Http响应流? 。 在我走得更远之前,我想弄清楚这种重复的记忆含义是什么; 不幸的是,谷歌和MSDN上的多次搜索已经化为乌有。

该库对要下载的文件的大小没有限制。 我的问题是,如果用户选择2GB文件,.NET 2.0中的MemoryStream实现是否足够智能,足以有效地使用PageFile和RAM,系统不会因VM崩溃而开始爬行? 此外,Jon Skeet对另一个问题的评论给了我一些思考 – 他断言即使在处理了MemoryStream之后,内存也不是100%免费的。 我如何以及何时确保实际释放内存? 它会根据系统的要求(和必要性)发布吗?

谢谢,Manoj

你把它保存到文件中,对吧? 为什么不按块保存块,随时更新哈希,然后只检查最后的哈希值? 我认为您不需要两次读取响应,也不需要缓冲它。 正如另一个答案所指出的,无论如何,当你超过1GB时,这将会失败。

不要忘记它以及MemoryStream的当前大小,每当它必须增长时,你最终会(暂时)结束新数组加上内存中的旧数组。 当然,如果您事先知道内容长度,这不会是一个问题,但是将它写入磁盘和散列仍然会更好。

MemoryStream由数组支持。 即使你有64位操作系统,这也不会超过1GB,因为框架不会分配更大的数组。

Afaik CLR托管堆不会分配任何大于2 GB的内容,而MemoryStream则由一个实时的,连续的byte []支持。 大对象堆不分配处理超过2GB,甚至不在x64上。

但是将整个文件存储在内存中只是为了计算哈希值似乎相当低技术。 您可以在收到字节时计算哈希值,块数为块。 每次IO完成后,您可以散列接收的字节,然后将写入提交给文件。 最后,你有哈希计算上传的文件,huraay。

顺便说一句,如果您寻求操作文件的代码,请避开任何包含单词ReadToEnd

 class Program { private static AutoResetEvent done = new AutoResetEvent(false); private static AsyncCallback _callbackReadStream; private static AsyncCallback _callbackWriteFile; static void Main(string[] args) { try { _callbackReadStream = new AsyncCallback(CallbackReadStream); _callbackWriteFile = new AsyncCallback(CallbackWriteFile); string url = "http://..."; WebRequest request = WebRequest.Create(url); request.Method = "GET"; request.BeginGetResponse(new AsyncCallback( CallbackGetResponse), request); done.WaitOne(); } catch (Exception e) { Console.Error.WriteLine(e.Message); } } private class State { public Stream ReponseStream { get; set; } public HashAlgorithm Hash { get; set; } public Stream FileStream { get; set; } private byte[] _buffer = new byte[16379]; public byte[] Buffer { get { return _buffer; } } public int ReadBytes { get; set; } public long FileLength {get;set;} } static void CallbackGetResponse(IAsyncResult ar) { try { WebRequest request = (WebRequest)ar.AsyncState; WebResponse response = request.EndGetResponse(ar); State s = new State(); s.ReponseStream = response.GetResponseStream(); s.FileStream = new FileStream("download.out" , FileMode.Create , FileAccess.Write , FileShare.None); s.Hash = HashAlgorithm.Create("MD5"); s.ReponseStream.BeginRead( s.Buffer , 0 , s.Buffer.Length , _callbackReadStream , s); } catch (Exception e) { Console.Error.WriteLine(e.Message); done.Set(); } } private static void CallbackReadStream(IAsyncResult ar) { try { State s = (State)ar.AsyncState; s.ReadBytes = s.ReponseStream.EndRead(ar); s.Hash.ComputeHash(s.Buffer, 0, s.ReadBytes); s.FileStream.BeginWrite( s.Buffer , 0 , s.ReadBytes , _callbackWriteFile , s); } catch (Exception e) { Console.Error.WriteLine(e.Message); done.Set(); } } static private void CallbackWriteFile(IAsyncResult ar) { try { State s = (State)ar.AsyncState; s.FileStream.EndWrite(ar); s.FileLength += s.ReadBytes; if (0 != s.ReadBytes) { s.ReponseStream.BeginRead( s.Buffer , 0 , s.Buffer.Length , _callbackReadStream , s); } else { Console.Out.Write("Downloaded {0} bytes. Hash(base64):{1}", s.FileLength, Convert.ToBase64String(s.Hash.Hash)); done.Set(); } } catch (Exception e) { Console.Error.WriteLine(e.Message); done.Set(); } } } 

我很确定你会得到一个OutOfMemoryException。 简单的尝试方法是尝试使用内存流将DVD ISO映像或其他内容读入内存。 如果你能读完整件事,那么你应该没问题。 如果你得到一个例外,那么你去吧。