Может ли непрерывная запись в поток без сбрасывания принудительно загружать его содержимое в кучу больших объектов?

В многопоточных настройках, таких как веб-сервер, в.NET, я обычно стараюсь избегать создания смежных блоков памяти, занимающих больше 85 КБ, так как это может привести к большой куче объектов, что может привести к проблемам с памятью.

Один из моих коллег-разработчиков использует цикл для записи в Response, OutputStreamбез Flushкроме как в конце цикла.

Правильно ли я считаю, что не FlushВхождение в цикл может привести к проблемам с памятью? Как я мог доказать это?

1 ответ

Решение

Это зависит от деталей реализации используемого потока. Из документа MSDN о базовом классе Stream

Поток - это абстракция последовательности байтов, такой как файл, устройство ввода / вывода, канал межпроцессного взаимодействия или сокет TCP/IP. Класс Stream и его производные классы предоставляют общее представление об этих различных типах ввода и вывода и изолируют программиста от конкретных деталей операционной системы и соответствующих устройств.

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

Я предполагаю, что вы находите этот ответ немного разочаровывающим, поэтому копайте немного глубже. Как вы говорите, ваш коллега использует Response.OutputStream давайте посмотрим, какова основная реализация этого свойства.

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

Так что с помощью Stream из чего-то в _httpWriter, Это поле содержит ссылку на экземпляр 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 в буфер, который является неуправляемой памятью. Эта память выделяется блоками около 16 Кб для интегрированного конвейера и около 31 Кб для остальных.

При этом я не ожидаю, что Stream выделит столько памяти, что его внутренние структуры окажутся на LOH, потому что, судя по всему, он делает только управляемые -> неуправляемые копии памяти.

Оставляет нам свою идею регулярно звонить Flush в петле. HttpResponseStream имеет эту реализацию:

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

где _writer является ранее обнаруженным HttpWriter, Его реализация

public override void Flush()
{
}

Это верно, вызов flush будет только тратить циклы процессора. Это не поможет потоку быстрее очистить свои буферы, несмотря на многообещающую документацию в MSDN.

Другие вопросы по тегам