可以在不刷新的情况下连续写入流,将其内容强制写入大对象堆吗?
Can continually writing to a stream without flushing force its contents onto the Large Object Heap?
在 .NET 中的网络服务器等多线程设置中,我通常会尽量避免创建占用超过 85KB 的连续内存块,因为这可能最终会出现在大对象堆上,从而导致内存问题。
我的一位开发人员正在使用循环写入 Response
。 OutputStream
,除了循环结束时没有 Flush
ing。
我认为不 Flush
在循环中会导致内存问题是否正确?我如何证明这一点?
这取决于正在使用的流的实现细节。来自 Stream base class
上的 MSDN 文档
A stream is an abstraction of a sequence of bytes, such as a file, an input/output device, an inter-process communication pipe, or a TCP/IP socket. The Stream class and its derived classes provide a generic view of these different types of input and output, and isolate the programmer from the specific details of the operating system and the underlying devices.
如果该流的实现者没有留下任何关于如何处理多个后续 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);
class 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 上,因为从它的外观来看,它只生成托管 -> 非托管内存副本。
给我们留下您在循环中定期调用 Flush
的想法。 HttpResponseStream
有这样的实现:
public override void Flush()
{
this._writer.Flush();
}
其中 _writer
是较早发现的 HttpWriter
。它的实现是
public override void Flush()
{
}
没错,调用flush只会浪费CPU个周期。尽管有希望 documentation in MSDN.
,但它不会帮助流更快地清除其缓冲区
在 .NET 中的网络服务器等多线程设置中,我通常会尽量避免创建占用超过 85KB 的连续内存块,因为这可能最终会出现在大对象堆上,从而导致内存问题。
我的一位开发人员正在使用循环写入 Response
。 OutputStream
,除了循环结束时没有 Flush
ing。
我认为不 Flush
在循环中会导致内存问题是否正确?我如何证明这一点?
这取决于正在使用的流的实现细节。来自 Stream base class
上的 MSDN 文档A stream is an abstraction of a sequence of bytes, such as a file, an input/output device, an inter-process communication pipe, or a TCP/IP socket. The Stream class and its derived classes provide a generic view of these different types of input and output, and isolate the programmer from the specific details of the operating system and the underlying devices.
如果该流的实现者没有留下任何关于如何处理多个后续 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);
class 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 上,因为从它的外观来看,它只生成托管 -> 非托管内存副本。
给我们留下您在循环中定期调用 Flush
的想法。 HttpResponseStream
有这样的实现:
public override void Flush()
{
this._writer.Flush();
}
其中 _writer
是较早发现的 HttpWriter
。它的实现是
public override void Flush()
{
}
没错,调用flush只会浪费CPU个周期。尽管有希望 documentation in MSDN.
,但它不会帮助流更快地清除其缓冲区