Приведет ли большой System.IO.MemoryStream к значительному увеличению использования памяти моим приложением? - PullRequest
3 голосов
/ 03 октября 2009

Я создаю библиотеку, которая позволяет пользователю загружать файлы с URL-адреса. Один из вариантов, который я рассматриваю, - позволить пользователю указать ожидаемую контрольную сумму MD5 для файла; Функция библиотеки GetFile (string url) гарантирует, что контрольная сумма для загруженного потока совпадает с контрольной суммой, указанной пользователем.

Зная, что NetworkStream, возвращаемый HttpWebResponse.GetResponseStream (), не доступен для поиска, я нашел способ дублировать поток благодаря ответам на этот вопрос: Как я могу дважды прочитать поток ответов Http в C #? . Прежде чем идти дальше, я хотел выяснить, каковы будут последствия этого дублирования для памяти; К сожалению, многочисленные поиски в Google и MSDN сошли на нет.

Библиотека не накладывает ограничений на размер загружаемого файла. Мой вопрос: если пользователь выбирает файл размером 2 ГБ, достаточно ли умна реализация MemoryStream в .NET 2.0, чтобы использовать PageFile и ОЗУ достаточно эффективно, чтобы система не начала сканировать из-за перерыва в работе виртуальной машины? Кроме того, комментарий Джона Скита по другому вопросу заставил меня задуматься - он утверждал, что даже после утилизации MemoryStream память не освобождается на 100%. Как и когда я могу убедиться, что память фактически освобождена? Будет ли он выпущен в зависимости от требований (и необходимости) системы?

Спасибо, Манодж

Ответы [ 4 ]

5 голосов
/ 03 октября 2009

Вы сохраняете это в файл, верно? Почему бы не сохранить его по частям, обновляя хеш по ходу, а затем просто проверять хеш в конце? Я не думаю, что вам нужно читать ответ дважды и не буферизировать его. Как указывает другой ответ, это не получится, если вы все равно получите более 1 ГБ.

Не забывайте, что наряду с текущим размером MemoryStream, каждый раз, когда он должен увеличиваться, вы получите (временно) новый массив плюс старый массив в памяти в то же время. Конечно, это не будет проблемой, если вы заранее знаете длину контента, но все равно было бы лучше просто записать его на диск и хэшировать по ходу работы.

4 голосов
/ 03 октября 2009

MemoryStream поддерживается массивом. Даже если у вас 64-битная ОС, это не сработает более чем на 1 ГБ, поскольку среда не будет выделять больший массив.

2 голосов
/ 03 октября 2009

Afaik управляемая куча CLR не будет выделять ничего больше, чем 2 ГБ, а MemoryStream поддерживается живым, непрерывным байтом []. Куча больших объектов не обрабатывает выделения более 2 ГБ, даже на x64.

Но хранить весь файл в памяти просто для вычисления хеша - это довольно скудно. Вы можете вычислить хеш по мере получения байтов, порциями за порциями. После каждого завершения ввода-вывода вы можете хэшировать полученные байты, а затем отправлять запись в файл. В конце у вас есть хэш, вычисленный и загруженный файл, 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();
        }

    }
}
0 голосов
/ 03 октября 2009

Я почти уверен, что вы получите исключение OutOfMemoryException. Простой способ попробовать - это прочитать ISO образ DVD или что-то в память, используя поток памяти. Если вы можете прочитать все это, то все будет в порядке. Если вы получите исключение, ну, вот и все.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...