C# 大型System.IO.MemoryStream是否会导致我的应用程序';内存使用量急剧增加?

C# 大型System.IO.MemoryStream是否会导致我的应用程序';内存使用量急剧增加?,c#,httpwebresponse,memorystream,C#,Httpwebresponse,Memorystream,我正在构建一个库,允许用户从URL下载文件。我正在考虑的一个选项是让用户为文件指定预期的MD5校验和;库的GetFile(stringurl)函数确保下载流的校验和与用户指定的校验和匹配 注意到HttpWebResponse.GetResponseStream()返回的网络流不可查找,我找到了一种复制流的方法,这要感谢对以下问题的回答:。在我进一步研究之前,我想弄清楚这种复制对记忆的影响;不幸的是,谷歌和MSDN上的多个搜索结果均为零 库对要下载的文件的大小没有限制。我的问题是,如果用户选择2G

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

注意到HttpWebResponse.GetResponseStream()返回的网络流不可查找,我找到了一种复制流的方法,这要感谢对以下问题的回答:。在我进一步研究之前,我想弄清楚这种复制对记忆的影响;不幸的是,谷歌和MSDN上的多个搜索结果均为零

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

谢谢,
Manoj

我很肯定你会得到一个OutOfMemory异常。简单的方法是尝试使用内存流将DVD ISO图像或其他内容读入内存。如果你能看懂整件事,那你就没事了。如果您遇到异常,那么就这样做。

您正在将其保存到一个文件中,对吗?为什么不一块一块地保存它,一边更新散列,然后在最后检查散列呢?我认为您不需要阅读两次响应,也不需要缓冲它。正如另一个答案所指出的那样,当你的内存超过1GB时,这将失败


不要忘记,除了
内存流的当前大小外,任何时候它必须增长时,您将(暂时)在内存中同时得到新数组和旧数组。当然,如果您事先知道内容长度,这不会是一个问题,但最好还是将其写入磁盘并在运行时进行散列。

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

Afaik CLR管理的堆不会分配任何大于2GB的内存,而MemoryStream由一个活动的连续字节[]支持。无法处理超过2GB的分配,即使在x64上也不行

但是仅仅为了计算散列而将整个文件存储在内存中似乎技术含量很低。您可以在接收字节时逐块计算散列。在每次IO完成后,您可以对接收到的字节进行散列,然后将写操作提交到文件。最后,您计算了散列,并上传了文件

顺便说一句,如果你想用代码来操作文件,请避开任何包含单词
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();
        }

    }
}

当他说它不是100%释放的时候,他是指自动垃圾收集吗?很可能是。您可以调用GC.Collect()手动强制垃圾收集,但这是一个不好的习惯,尤其是当调用它的方法将被频繁调用时。我的意思是,如果在调用Dispose后保持
MemoryStream
,数据仍然在那里-如果调用
ToArray
,你仍然可以得到数据。在
MemoryStream
关闭之前,阵列不符合收集条件。换句话说,在MemoryStream上调用Dispose以使内存可供GC使用是一件愚蠢的差事:)我没有将流的内容写入磁盘的自由(即使是暂时的)因为库没有被授权将数据写入用户的文件系统。我当时不明白。您说过您的组件下载文件。它如何下载它而不写入磁盘?你是说你想让浏览器处理下载,但又不知何故获得了md5校验和的下载流?还是你想下载两次,一次由浏览器下载,一次由你的校验和代码下载?要计算校验和,您不必在任何地方写入校验和,只需在每个缓冲区上调用ComputeHash,然后丢弃缓冲区;考虑的选项之一是使用一个标志重载GetObject,该标志指定代表用户为流计算MD5摘要:stream GetObject(字符串uri,bool fVerifyDigest);网络文件服务器为正在下载的对象提供MD5摘要,作为HTTPWebResponse头之一。其思想是计算流的散列,并将其与服务器返回的值进行比较。有道理吗?我明白了。您应该返回自己的类,该类派生自实现此功能的流(计算散列)。类似于DeflateStream、GZipStream或CryptoStream以及其他流派生类的工作方式。您可以构造流来包装HttpResult流。您重写读取,并在实现中从http流请求字节,将它们添加到哈希,然后将它们返回给调用方;流被传递给用户,用户可以根据数据做任何他们认为合适的事情。计算散列的选项提供给用户是一个很好的选择,但是从手头任务的复杂性来看,我将重新考虑向库中添加此功能。谢谢