如何重用MemoryStream

发布于 2024-10-10 22:06:20 字数 838 浏览 5 评论 0原文

我的代码使用 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;
}

My code uses MemoryStream to serialize/deserialize objects to/from the network. I would like to re-use a single MemoryStream in my class, rather than create a new one each time I need
to send something over the wire.

Does anyone know how to do this?

Code snippet:

// 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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

[旋木] 2024-10-17 22:06:20

首先,您的序列化方法有一个错误:

请注意,缓冲区包含可能未使用的已分配字节。例如,如果将字符串“test”写入 MemoryStream 对象,则从 GetBuffer 返回的缓冲区长度为 256,而不是 4,其中有 252 个字节未使用。要仅获取缓冲区中的数据,请使用ToArray方法;但是,ToArray 在内存中创建数据的副本。

即数组返回大于序列化数据

对于反序列化,您可以构造一个使用传入数组的内存流,因此它不会分配内部缓冲区。但除非你有基准测试表明内存流分配确实是一个瓶颈,否则我不会打扰。

如果您确实想优化内存分配,则需要重用byte[]缓冲区。这尤其意味着修改 API 以处理数组的子部分,因此消息大小和数组大小不需要相同。

以下是可以随时更改的实现细节(并且自从我阅读后可能已经更改):
如果缓冲区最终没有出现在大对象堆上,那当然不值得打扰。如果对象很小,它们将在下一个 Gen0 收集中以低廉的价格收集。另一方面,大对象堆直接以 Gen2 结束。大于 250kB 的 AFAIR 对象被分配在那里。

当然,在不缩小缓冲区的情况下重用它们可能会导致内存泄漏。

First of all your serialize method has a bug:

Note that the buffer contains allocated bytes which might be unused. For example, if the string "test" is written into the MemoryStream object, the length of the buffer returned from GetBuffer is 256, not 4, with 252 bytes unused. To obtain only the data in the buffer, use the ToArray method; however, ToArray creates a copy of the data in memory.

i.e. the array returns is larger than the serialized data

For deserializing you can construct a memory stream which uses the passed in array, so it won't allocate internal buffers. But unless you have benchmarks which show that memory stream allocation is really a bottleneck I wouldn't bother.

If you really want to optimize your memory allocations you'll need to reuse the byte[] buffers. This in particular means modifying the api to work with subsections of arrays so the message size and array size don't need to be identical.

The following are implementation details which can change at any time(and might already have changed since I read about it):
It's surely not worth bothering if the buffers don't end up on the large object heap. If the objects are small they'll be cheaply collected on the next Gen0 collection. The large object heap on the other hand directly ends up in Gen2. AFAIR objects >250kB are allocated there.

And of course reusing the buffers without ever shrinking them can be a memory leak.

那些过往 2024-10-17 22:06:20

重复使用相同的 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;
        }
    }
}

Reusing same MemoryStream does not give you any performance benefit.

There is a reason why MemoryStream does not have a clear. Because it would be more expensive to clear it than to create a new one.

If you look at the internals of the class, you can see that it allocates a buffer, and when writing, if its buffer gets full, it allocates new buffer and copy existing bytes and then carries on. So in a way the buffer is immutable.

This can be seen here at the setting of the capacity which is called by EnsureCapacity() at the time of writing:

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;
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文