可以连续写入流而不刷新强制其内容到大型对象堆上

本文关键字:大型 对象 连续 刷新 | 更新日期: 2023-09-27 17:57:14

在多线程设置(如 Web 服务器)中,在 .NET 中,我通常尽量避免创建占用更多 85KB 的连续内存块,因为这最终可能会出现在大型对象堆上,这可能会导致内存问题。

我的一位开发人员同事正在使用循环写入ResponseOutputStream ,除了在循环结束时外,没有Flush

我是否正确认为不Flush循环会导致内存问题? 我怎么能证明这一点?

可以连续写入流而不刷新强制其内容到大型对象堆上

这取决于所使用的流的实现细节。从流基类上的 MSDN 文档

流是字节序列的抽象,例如文件、输入/输出设备、进程间通信管道或 TCP/IP 套接字。Stream 类及其派生类提供这些不同类型的输入和输出的通用视图,并将程序员与操作系统和基础设备的特定详细信息隔离开来。

如果该流的实现者没有留下任何关于如何处理多个后续Write调用的额外指导,我们应该假设它为您处理这些刷新详细信息。

我想你觉得这个答案有点令人失望,所以深入挖掘一下。正如您所说,您的同事正在使用Response.OutputStream让我们看看该属性的基础实现是什么。

get
{
    if (!this.UsingHttpWriter)
    {
        throw new HttpException(SR.GetString("OutputStream_NotAvail"));
    }
    return this._httpWriter.OutputStream;
}

所以它正在使用_httpWriter中某些东西的Stream.该字段原来包含对HttpWriter实例的引用。它是OutputStream属性在构造函数中初始化:

this._stream = new HttpResponseStream(this);

HttpResponseStream是内部的,但我们可以使用 ILSpy 来撬开它。它的Write方法将实现推迟到这个HttpWriter的方法:

internal void WriteFromStream(byte[] data, int offset, int size)
{
    if (this._charBufferLength != this._charBufferFree)
    {
        this.FlushCharBuffer(true);
    }
    this.BufferData(data, offset, size, true);
    if (!this._responseBufferingOn)
    {
        this._response.Flush();
    }
}

如您所见,byte[] 数据正在传递给一个方法,该方法在帮助HttpResponseUnmanagedBufferElement的帮助下进一步复制和存储数据,该方法将字节复制到内存中,并Marshal.Copy到非托管内存的缓冲区。该内存似乎以大约 16K 的块分配给集成管道,其余的大约 31K 的块。

因此,我不希望 Stream 分配太多内存,以至于其内部结构最终位于 LOH 上,因为从外观上看,它只会制作托管>非托管内存副本。

让我们知道您的想法,定期打电话给循环中的FlushHttpResponseStream具有以下实现:

public override void Flush()
{
    this._writer.Flush();
}

其中_writer是较早发现的HttpWriter。它的实现是

public override void Flush()
{
}

没错,调用刷新只会浪费 CPU 周期。它不会帮助流更快地清除其缓冲区,尽管 MSDN 中有很有希望的文档。