Memorystream и куча больших объектов
Мне нужно перенести большие файлы между компьютерами через ненадежные соединения с помощью WCF.
Потому что я хочу, чтобы иметь возможность возобновить файл, и я не хочу ограничиваться в моем файлеize WCF, я разбиваю файлы на 1 Мб. Эти "куски" транспортируются как поток. Что работает довольно хорошо, пока.
Мои шаги:
- открыть файл
- читать фрагмент из файла в байт [] и создавать memystream
- часть передачи
- назад до 2. до отправки всего файла
Моя проблема находится на шаге 2. Я предполагаю, что когда я создаю поток памяти из массива байтов, он попадает в LOH и в конечном итоге выдает исключение outofmemory. Я не мог создать эту ошибку, возможно, я ошибаюсь в своем предположении.
Теперь я не хочу отправлять байт [] в сообщение, поскольку WCF скажет мне, что размер массива слишком велик. Я могу изменить максимально допустимый размер массива и/или размер моего куска, но я надеюсь, что есть еще одно решение.
Мой фактический вопрос (ы):
- Будет ли мое текущее решение создавать объекты в LOH и это вызовет у меня проблемы?
- Есть ли лучший способ решить эту проблему?
Btw: На стороне приема я просто читаю мелкие куски из поступающего потока и записываю их непосредственно в файл, поэтому не задействованы большие массивы байтов.
Edit:
текущее решение:
for (int i = resumeChunk; i < chunks; i++)
{
byte[] buffer = new byte[chunkSize];
fileStream.Position = i * chunkSize;
int actualLength = fileStream.Read(buffer, 0, (int)chunkSize);
Array.Resize(ref buffer, actualLength);
using (MemoryStream stream = new MemoryStream(buffer))
{
UploadFile(stream);
}
}
Ответы
Ответ 1
Я надеюсь, что все в порядке. Это мой первый ответ на StackOverflow.
Да, если ваш chunksize превышает 85000 байт, тогда массив будет выделен на кучу большого объекта. Вы, скорее всего, не исчерпаете память очень быстро, поскольку вы выделяете и освобождаете смежные области памяти, которые имеют одинаковый размер, поэтому, когда память заполняет среду выполнения, она может поместиться в новый кусок в старую, исправленную область памяти.
Я был бы немного обеспокоен вызовом Array.Resize, так как это создаст другой массив (см. http://msdn.microsoft.com/en-us/library/1ffy6686(VS.80).aspx). Это необязательный шаг, если actualLength == Chunksize, как и для всех, кроме последнего фрагмента. Поэтому я бы как минимум предположил:
if (actualLength != chunkSize) Array.Resize(ref buffer, actualLength);
Это должно удалить много распределений. Если actualSize не совпадает с chunkSize, но все еще > 85000, то новый массив также будет выделен на кучу больших объектов, что потенциально может привести к фрагментации и, возможно, вызвать явные утечки памяти. По-моему, все еще требуется много времени, чтобы на самом деле закончилась нехватка памяти, поскольку утечка будет довольно медленной.
Я думаю, что лучшей реализацией будет использование своего рода буферного пула для предоставления массивов. Вы можете свернуть свои собственные (это было бы слишком сложно), но WCF предоставит вам один. Я немного переписал ваш код, чтобы получить от этого:
BufferManager bm = BufferManager.CreateBufferManager(chunkSize * 10, chunkSize);
for (int i = resumeChunk; i < chunks; i++)
{
byte[] buffer = bm.TakeBuffer(chunkSize);
try
{
fileStream.Position = i * chunkSize;
int actualLength = fileStream.Read(buffer, 0, (int)chunkSize);
if (actualLength == 0) break;
//Array.Resize(ref buffer, actualLength);
using (MemoryStream stream = new MemoryStream(buffer))
{
UploadFile(stream, actualLength);
}
}
finally
{
bm.ReturnBuffer(buffer);
}
}
это предполагает, что реализация UploadFile может быть переписана, чтобы взять int для no. байтов для записи.
Я надеюсь, что это поможет
Джо
Ответ 2
Я не настолько уверен в первой части вашего вопроса, но по-лучшему - считали ли вы BITS? Это позволяет загружать файлы через http. Вы можете предоставить ему http://или файл://URI. Он возобновляется с того момента, когда он был прерван и загружен в куски байтов с использованием метода RANGE в HTTP HEADER. Он используется Windows Update. Вы можете подписаться на события, которые предоставляют информацию о прогрессе и завершении.
Ответ 3
См. также RecyclableMemoryStream.
Из в этой статье:
Microsoft.IO.RecyclableMemoryStream - это замена MemoryStream, которая обеспечивает превосходное поведение для критически важных систем. В частности, он оптимизирован для выполнения следующих действий:
- Исключить выделение больших кусков объектов с помощью пустых буферов
- Начать гораздо меньше генераторов 2 GC и тратить гораздо меньше времени на паузу из-за GC
- Избегайте утечек памяти, имея размер ограниченного пула
- Избегать фрагментации памяти.
- Обеспечить отличную отлаживаемость
- Предоставить показатели для отслеживания производительности
Ответ 4
Я придумал другое решение для этого, дайте мне знать, что вы думаете!
Поскольку я не хочу иметь большие объемы данных в памяти, я искал элегантный способ временного хранения массивов байтов или потока.
Идея состоит в создании временного файла (для этого вам не нужны конкретные права), а затем используйте его подобно потоку памяти. Создание класса Disposable очистит временный файл после его использования.
public class TempFileStream : Stream
{
private readonly string _filename;
private readonly FileStream _fileStream;
public TempFileStream()
{
this._filename = Path.GetTempFileName();
this._fileStream = File.Open(this._filename, FileMode.OpenOrCreate, FileAccess.ReadWrite);
}
public override bool CanRead
{
get
{
return this._fileStream.CanRead;
}
}
// and so on with wrapping the stream to the underlying filestream
...
// finally overrride the Dispose Method and remove the temp file
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
this._fileStream.Close();
this._fileStream.Dispose();
try
{
File.Delete(this._filename);
}
catch (Exception)
{
// if something goes wrong while deleting the temp file we can ignore it.
}
}