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

时间:2009-10-02 20:24:31

标签: c# httpwebresponse memorystream

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

知道HttpWebResponse.GetResponseStream()返回的NetworkStream不可寻找,我找到了一种复制流的方法,这要归功于这个问题的答案:How can I read an Http response stream twice in C#?。在我走得更远之前,我想弄清楚这种重复的记忆含义是什么;不幸的是,Google和MSDN上的多次搜索都化为乌有。

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

谢谢, 的Manoj

4 个答案:

答案 0 :(得分:5)

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

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

答案 1 :(得分:4)

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

答案 2 :(得分:2)

Afaik CLR托管堆不会分配大于2 GB的内容,而MemoryStream则由一个实时的,连续的byte []支持。 Large Object Heap的分配处理不超过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();
        }

    }
}

答案 3 :(得分:0)

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

相关问题