Когда GetBuffer () в MemoryStream может быть полезен?
Я знал, что GetBuffer()
на потоке памяти в C#/.NET должен использоваться с осторожностью, потому что, как описывают документы здесь, в конце могут быть неиспользуемые байты, поэтому вы должны быть уверены, что смотрите только на первый поток памяти.Длина байт в буфере.
ToArray()
, Вы можете увидеть следующее:
public virtual byte[] ToArray()
{
byte[] dst = new byte[this._length - this._origin];
Buffer.InternalBlockCopy(this._buffer, this._origin, dst, 0,
this._length - this._origin);
return dst;
}
Так что делать что-либо с буфером возвращенный GetBuffer()
, вам действительно нужно знать _origin. Единственная проблема в том, что оригин-частное лицо, и до него никак не добраться...
Итак, мой вопрос - какая польза GetBuffer()
на MemoryStream()
без некоторого априорного знания о том, как был построен поток памяти (который является тем, что задает _origin)?
(именно этот конструктор, и только этот конструктор, задает origin-for, когда вы хотите, чтобы поток памяти вокруг массива байтов начинался с определенного индекса в байте массив:
public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible)
)
7 ответов:
Если вы действительно хотите получить доступ к внутреннему значению _origin, вы можете использовать MemoryStream.Seek(0, SeekOrigin.Начинайте) звонить. Возвращаемое значение будет точно таким же, как значение _origin.
Ответ находится в GetBuffer() MSDN doc , возможно, вы его пропустили.
При создании
MemoryStream
без предоставления массива байтов (byte[]
) :Другими словами, поток памяти будет ссылаться наЭто создает возможность расширения емкости обнуляются.
byte[]
с соответствующим размером, когда вызовWrite
будет выполнен в потоке.Таким образом, с помощью
GetBuffer()
Вы можете напрямую получить доступ к базовому массиву и прочитать его.Это может быть полезно, когда вы находитесь в ситуации, когда вы получите поток, не зная его размера. Если полученный поток обычно очень большой, то вызов
GetBuffer()
будет намного быстрее, чем вызовToArray()
, которые копируют данные под капотом, см. ниже.Чтобы получить только данные в буфере, используйте метод ToArray; однако ToArray создает копию данных в памяти.
интересно, в какой момент Вы могли бы позвонить GetBuffer (), чтобы получить мусор данные в начале, это может быть между двумя вызовами
Write
, где данные из первого были бы собраны мусор, но я не уверен, что это может произойти.
ToArray () является альтернативой GetBuffer (). Однако ToArray() создает копию объекта в памяти. Если байт больше 80000, объект будет помещен в большую кучу объектов (LOH). Пока ничего особенного. Однако GC не очень хорошо обрабатывает LOH и объекты в нем (память не освобождается, как вы ожидаете). Из-за этого может возникнуть исключение OutOfMemoryException. Решение состоит в том, чтобы либо вызвать GC.Собрать () так, чтобы эти объекты были собраны или использовать GetBuffer () и создать несколько меньших (менее 80000 байт) объектов-те не пойдут в LOH, и память будет освобождена, как и ожидалось GC.
Существует третий (лучший) вариант, который заключается в использовании только потоков, например, считывании всех байтов из потока памяти и прямой записи их в HttpResponse.OutputStream (снова используя байтовый массив
В качестве резюме мы можем сказать, что когда копия объекта в памяти не является при желании вам придется избегать ToArray (), и в этих случаях GetBuffer () может пригодиться, но не может быть лучшим решением.
Это может быть полезно, если вы используете низкоуровневый API, который принимает сокет
ArraySegment
, например .Отправить . Вместо вызоваToArray
, который создаст еще одну копию массива, вы можете создать сегмент:var segment=new ArraySegment<byte>(stream.GetBuffer(), 0, stream.Position);
И затем передайте это в метод
Send
. Для больших данных это позволит избежать выделения нового массива и копирования в него, что может быть дорого.
GetBuffer()
всегда предполагается, что вы знаете структуру данных, подаваемых в строку (и это ее использование). Если вы хотите получить данные из потока, вы всегда должны использовать один из предоставленных методов (например,ToArray()
).Что-то вроде этого можно использовать, но единственный случай, который я мог бы сейчас придумать, - это какая-то фиксированная структура или виртуальная файловая система, находящаяся в потоке. Например, в текущей позиции вы считываете смещение для файла, находящегося внутри потока. Затем вы создаете новый объект потока, основанный на буфере этого потока, но с другим
_origin
. Это избавляет вас от копирования всех данных для нового объекта, что может позволить вам сэкономить много памяти. Это избавляет вас от необходимости носить исходный буфер в качестве ссылки с собой, потому что вы всегда можете получить его еще раз.
В .NET 4.6 появился новый API,
bool MemoryStream.TryGetBuffer(out ArraySegment<byte> buffer)
это похоже по духу на.GetBuffer()
. Этот метод вернет значениеArraySegment
это включает в себя информацию_origin
, Если это возможно.Смотритеэтот вопрос для получения подробной информации о том, когда
.TryGetBuffer()
вернет true и заполнит out param полезной информацией.
Самый важный момент из
GetBuffer
документация MSDN , Кроме того, что она Не создает копию данных, заключается в том, что она возвращает массив, который имеет неиспользуемые байты:Обратите внимание, что буфер содержит выделенные байты, которые могут быть неиспользуемыми. Например, если строка "test" записана в объект MemoryStream, длина буфера, возвращаемого из GetBuffer, составляет 256, а не 4, при этом 252 байта не используются. Чтобы получить только данные в буфере, используйте Метод ToArray; однако ToArray создает копию данных в памяти.Таким образом, если вы действительно хотите избежать создания копии из-за ограничений памяти, вы должны быть осторожны, чтобы не отправить весь массив изGetBuffer
по проводу или не сбросить его в файл или вложение, потому что этот буфер растет на степени 2 всякий раз, когда он заполнен и почти всегда имеет много неиспользуемых байтов в конце.