хотите повторно использовать MemoryStream


Мой код использует MemoryStream для сериализации / десериализации объектов в / из сети. Я хотел бы повторно использовать один поток памяти в моем классе, а не создавать новый каждый раз, когда мне нужно чтобы послать что-нибудь по проводу.

Кто-нибудь знает, как это сделать?

Фрагмент кода:

    // Serialize object to buffer
    public  byte[] Serialize(object value)
    {
        if (value == null)
            return null;
      MemoryStream _memoryStream = new MemoryStream();

        _memoryStream.Seek(0, 0);
        _bf.Serialize(_memoryStream, value);
        return _memoryStream.GetBuffer();
    }

    // Deserialize buffer to object
    public  object Deserialize(byte[] someBytes)
    {         
        if (someBytes == null)
            return null;
        MemoryStream _memoryStream = new MemoryStream();
        _memoryStream.Write(someBytes, 0, someBytes.Length);
        _memoryStream.Seek(0, 0);
        var de = _bf.Deserialize(_memoryStream);
        return de;
    }

Спасибо!

2 7

2 ответа:

Во-первых, ваш метод сериализации имеет ошибку:

Обратите внимание, что буфер содержит выделенные байты, которые могут быть неиспользуемыми. Например, если строка "test" записана в объект MemoryStream, длина буфера, возвращаемого из GetBuffer, составляет 256, а не 4, при этом 252 байта не используются. Чтобы получить только данные в буфере, используйте метод ToArray; однако ToArray создает копию данных в памяти.

То есть массив возвращает больше, чем сериализованный данные

Для десериализации можно построить поток памяти, который использует переданный массив, поэтому он не будет выделять внутренние буферы. Но если у вас нет тестов, которые показывают, что распределение потока памяти действительно является узким местом, я бы не беспокоился.

Если вы действительно хотите оптимизировать распределение памяти, вам нужно будет повторно использовать буферы byte[]. Это, в частности, означает изменение api для работы с подразделами массивов, так что размер сообщения и размер массива не должны быть идентичный.

Ниже приведены детали реализации, которые могут измениться в любое время(и, возможно, уже изменились с тех пор, как я прочитал об этом):
Конечно, не стоит беспокоиться, если буферы не попадают в большую кучу объектов. Если объекты небольшие, они будут дешево собраны в следующей коллекции Gen0. Большая куча объектов, с другой стороны, непосредственно заканчивается в Gen2. Там расположены объекты AFAIR >250kB.

И, конечно же, повторно использовать буферы, никогда не сокращаясь это может быть утечка памяти.

Повторное использование того же самого MemoryStream не дает вам никакого преимущества в производительности.

Есть причина, по которой MemoryStream не имеет ясного. Потому что очистить его будет дороже, чем создать новый.

Если вы посмотрите на внутренние части класса, вы увидите, что он выделяет буфер, и при записи, если его буфер заполняется, он выделяет новый буфер и копирует существующие байты, а затем продолжает. Таким образом, в некотором смысле буфер является неизменяемым.

Это можно увидеть здесь на установка емкости, которая вызывается EnsureCapacity() на момент написания:

public virtual int Capacity
{
    get
    {
        if (!this._isOpen)
        {
            __Error.StreamIsClosed();
        }
        return (this._capacity - this._origin);
    }
    [SecuritySafeCritical]
    set
    {
        if (value < this.Length)
        {
            throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
        }
        if (!this._isOpen)
        {
            __Error.StreamIsClosed();
        }
        if (!this._expandable && (value != this.Capacity))
        {
            __Error.MemoryStreamNotExpandable();
        }
        if (this._expandable && (value != this._capacity))
        {
            if (value > 0)
            {
                byte[] dst = new byte[value];
                if (this._length > 0)
                {
                    Buffer.InternalBlockCopy(this._buffer, 0, dst, 0, this._length);
                }
                this._buffer = dst;
            }
            else
            {
                this._buffer = null;
            }
            this._capacity = value;
        }
    }
}