IIS应用程序池进程使用大量内存

发布于 2024-11-27 03:29:05 字数 582 浏览 1 评论 0原文

我的 IIS 应用程序池进程之一遇到了一个非常奇怪的问题。我最近收到 System.OutOfMemoryException 错误,并且一直试图弄清楚到底发生了什么。基本上我有一个脚本,它使用 Web 服务从我们的 DAM 获取文件。然后它检查文件并将其存储为字节数组,然后使用响应输出文件。我遇到的唯一问题是 PDF,当它们超过 20MB 时,现在看来它们有时会导致错误。如果我增加应用程序池中的内存,它会暂时解决问题。我观察了 w3wp.exe 进程,发现有时当我运行这个脚本时,它会将内存增加到 400MB,我们拥有的最大文件是 45MB,这会导致这种类型的行为发生。这个问题似乎每天晚上都会消失,早上它会工作一段时间,然后又开始做同样的事情。该应用程序是 c# asp.net 应用程序。它在共享点内部运行。

在观看该服务一段时间后,我确实注意到,由于这些 PDF 是在浏览器窗口中呈现的,因此在文件完全下载之前,它不会从内存中释放。这是有道理的,但我可以看到这在某种程度上是我的问题。如果我有几个人加载文件,平均(没有文件下载)内存使用量为 385,000 kb。它很容易达到 900,000-1,100,000 KB,这是应用程序池的限制。

我并不是在寻找确切的答案,而是在寻找前进的方向,因为我没有想法。

I have a very weird problem with one of my IIS application pool processes. Ive been getting a System.OutOfMemoryException error lately and ive been trying to figure out exactly what is going on. Basically I have a script that uses a web service to get a file from our DAM. It then checks the file stores it a byte array, then uses the Response to output the file. The only one ive been having problems with is the PDF's when they are over 20MB now it seems that they cause an error sometimes. If I increase the memory in the app pool it fixes the problem temporarily. I watched the w3wp.exe process and seen that sometimes when I run this script it increase the memory up to 400MB the largest file we have is 45MB what would be causing this type of behavior to happen. The problem seems to go away every night and in the morning it will work for a while and then start doing the same thing again. This application is c# asp.net application. It run inside of sharepoint.

After watching the service for a while I did notice that since these PDF's are rendered in the browser window that until the file downloads completely it doesn't release from memory. which makes sense but I can see that this is somewhat of my problem. If I have several people loading the file, with a average (no file downloading) memory usage at 385,000 kb.It can easily get to 900,000-1,100,000 KB which is the limit of the application pool.

Im not so much looking for an exact answer but more like a direction to head because I am all out of ideas.

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

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

发布评论

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

评论(2

我不咬妳我踢妳 2024-12-04 03:29:05

当您将文件数据作为字节数组放入内存时,会给 Web 服务器带来很大的压力。

您应该尝试将文件流分块写入响应流,而不是将整个文件数据存储在字节数组中。

伪示例:

context.Response.Buffer = false;

byte[] buffer   = new byte[4096];
int bytesRead   = 0;

using(var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
    while ((bytesRead = stream.Read(buffer, 0 , buffer.Length)) > 0)
    {
        context.Response.OutputStream.Write(buffer, 0, buffer.Length);
        context.Response.OutputStream.Flush();
    }
}

这里的想法是,您只在每次读取文件流时将文件数据块放入内存,然后将其写入响应。请注意,响应缓冲已被禁用,并且您可以将文件流替换为另一个流数据源(我在从 SQL 数据库读取二进制数据时使用了这种方法)。

编辑:(响应如何将数据从 SQL 流式传输到 HTTP 响应)

为了从 SQL Server 数据库表(例如 varbinary(max) 列)流式传输数据,您可以在 SqlCommand 上使用顺序访问:

#region WriteResponse(HttpContext context, Guid id)
/// <summary>
/// Writes the content for a media resource with the specified <paramref name="id"/> 
/// to the response stream using the appropriate content type and length.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> to write content to.</param>
/// <param name="id">The unique identifier assigned to the media resource.</param>
private static void WriteResponse(HttpContext context, Guid id)
{
    using(var connection = ConnectionFactory.Create())
    {
        using (var command = new SqlCommand("[dbo].[GetResponse]", connection))
        {
            command.CommandType = CommandType.StoredProcedure;

            command.Parameters.Add("@Id", SqlDbType.UniqueIdentifier);
            command.Parameters.AddReturnValue();

            command.Parameters["@Id"].Value = id;

            command.Open();

            using(var reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
            {
                if(reader.Read())
                {
                    WriteResponse(context, reader);
                }
            }
        }
    }
}
#endregion

#region WriteResponse(HttpContext context, SqlDataReader reader)
/// <summary>
/// Writes the content for a media resource to the response stream using the supplied <paramref name="reader"/>.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> to write content to.</param>
/// <param name="reader">The <see cref="SqlDataReader"/> to extract information from.</param>
private static void WriteResponse(HttpContext context, SqlDataReader reader)
{
    if (context == null || reader == null)
    {
        return;
    }

    DateTime expiresOn      = DateTime.UtcNow;
    string contentType      = String.Empty;
    long contentLength      = 0;
    string fileName         = String.Empty;
    string fileExtension    = String.Empty;

    expiresOn               = reader.GetDateTime(0);
    fileName                = reader.GetString(1);
    fileExtension           = reader.GetString(2);
    contentType             = reader.GetString(3);
    contentLength           = reader.GetInt64(4);

    context.Response.AddHeader("Content-Disposition", String.Format(null, "attachment; filename={0}", fileName));

    WriteResponse(context, reader, contentType, contentLength);

    ApplyCachePolicy(context, expiresOn - DateTime.UtcNow);
}
#endregion

#region WriteResponse(HttpContext context, SqlDataReader reader, string contentType, long contentLength)
/// <summary>
/// Writes the content for a media resource to the response stream using the 
/// specified reader, content type and content length.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> to write content to.</param>
/// <param name="reader">The <see cref="SqlDataReader"/> to extract information from.</param>
/// <param name="contentType">The content type of the media.</param>
/// <param name="contentLength">The content length of the media.</param>
private static void WriteResponse(HttpContext context, SqlDataReader reader, string contentType, long contentLength)
{
    if (context == null || reader == null)
    {
        return;
    }

    int ordinal     = 5;
    int bufferSize  = 4096 * 1024; // 4MB
    byte[] buffer   = new byte[bufferSize];
    long value;
    long dataIndex;

    context.Response.Buffer         = false;
    context.Response.ContentType    = contentType;
    context.Response.AppendHeader("content-length", contentLength.ToString());

    using (var writer = new BinaryWriter(context.Response.OutputStream))
    {
        dataIndex   = 0;
        value       = reader.GetBytes(ordinal, dataIndex, buffer, 0, bufferSize);

        while(value == bufferSize)
        {
            writer.Write(buffer);
            writer.Flush();

            dataIndex   += bufferSize;
            value       = reader.GetBytes(ordinal, dataIndex, buffer, 0, bufferSize);
        }

        writer.Write(buffer, 0, (int)value);
        writer.Flush();
    }
}
#endregion

When you bring the file data into memory as an array of bytes, you are putting a lot of pressure on the web server.

Instead of storing the entire file data in an array of bytes, you should try writing the file stream into the reponse stream in chunks.

Pseudo example:

context.Response.Buffer = false;

byte[] buffer   = new byte[4096];
int bytesRead   = 0;

using(var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
    while ((bytesRead = stream.Read(buffer, 0 , buffer.Length)) > 0)
    {
        context.Response.OutputStream.Write(buffer, 0, buffer.Length);
        context.Response.OutputStream.Flush();
    }
}

The idea here being that you only bring a chunk of the file data into memory on each read of the file stream and then write it to the response. Note that response buffering has been disabled, and that you can substitute using a file stream with another Stream data source (I have used this approach while reading binary data from a SQL database).

Edit: (Response to how to stream data from SQL to HTTP Response)

In order to stream data from a SQL server database table (e.g. a varbinary(max) column), you use sequential access on SqlCommand:

#region WriteResponse(HttpContext context, Guid id)
/// <summary>
/// Writes the content for a media resource with the specified <paramref name="id"/> 
/// to the response stream using the appropriate content type and length.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> to write content to.</param>
/// <param name="id">The unique identifier assigned to the media resource.</param>
private static void WriteResponse(HttpContext context, Guid id)
{
    using(var connection = ConnectionFactory.Create())
    {
        using (var command = new SqlCommand("[dbo].[GetResponse]", connection))
        {
            command.CommandType = CommandType.StoredProcedure;

            command.Parameters.Add("@Id", SqlDbType.UniqueIdentifier);
            command.Parameters.AddReturnValue();

            command.Parameters["@Id"].Value = id;

            command.Open();

            using(var reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
            {
                if(reader.Read())
                {
                    WriteResponse(context, reader);
                }
            }
        }
    }
}
#endregion

#region WriteResponse(HttpContext context, SqlDataReader reader)
/// <summary>
/// Writes the content for a media resource to the response stream using the supplied <paramref name="reader"/>.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> to write content to.</param>
/// <param name="reader">The <see cref="SqlDataReader"/> to extract information from.</param>
private static void WriteResponse(HttpContext context, SqlDataReader reader)
{
    if (context == null || reader == null)
    {
        return;
    }

    DateTime expiresOn      = DateTime.UtcNow;
    string contentType      = String.Empty;
    long contentLength      = 0;
    string fileName         = String.Empty;
    string fileExtension    = String.Empty;

    expiresOn               = reader.GetDateTime(0);
    fileName                = reader.GetString(1);
    fileExtension           = reader.GetString(2);
    contentType             = reader.GetString(3);
    contentLength           = reader.GetInt64(4);

    context.Response.AddHeader("Content-Disposition", String.Format(null, "attachment; filename={0}", fileName));

    WriteResponse(context, reader, contentType, contentLength);

    ApplyCachePolicy(context, expiresOn - DateTime.UtcNow);
}
#endregion

#region WriteResponse(HttpContext context, SqlDataReader reader, string contentType, long contentLength)
/// <summary>
/// Writes the content for a media resource to the response stream using the 
/// specified reader, content type and content length.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> to write content to.</param>
/// <param name="reader">The <see cref="SqlDataReader"/> to extract information from.</param>
/// <param name="contentType">The content type of the media.</param>
/// <param name="contentLength">The content length of the media.</param>
private static void WriteResponse(HttpContext context, SqlDataReader reader, string contentType, long contentLength)
{
    if (context == null || reader == null)
    {
        return;
    }

    int ordinal     = 5;
    int bufferSize  = 4096 * 1024; // 4MB
    byte[] buffer   = new byte[bufferSize];
    long value;
    long dataIndex;

    context.Response.Buffer         = false;
    context.Response.ContentType    = contentType;
    context.Response.AppendHeader("content-length", contentLength.ToString());

    using (var writer = new BinaryWriter(context.Response.OutputStream))
    {
        dataIndex   = 0;
        value       = reader.GetBytes(ordinal, dataIndex, buffer, 0, bufferSize);

        while(value == bufferSize)
        {
            writer.Write(buffer);
            writer.Flush();

            dataIndex   += bufferSize;
            value       = reader.GetBytes(ordinal, dataIndex, buffer, 0, bufferSize);
        }

        writer.Write(buffer, 0, (int)value);
        writer.Flush();
    }
}
#endregion
蓝眸 2024-12-04 03:29:05

反对派对于处理文件本身有很好的建议。我还会查看您在网络处理中可能保留的参考资料。是否在会话状态或应用程序状态中存储任何内容?如果是这样,请仔细跟踪它们,以确保它们不会依次指向与处理文件有关的页面或其他内容。

我提到这一点是因为几年前我们因将对象置于应用程序状态而导致了一次令人讨厌的“泄漏”。事实证明,该对象订阅了页面事件,并且由于该对象从未死亡,所有页面也从未死亡!哎呀

Oppositional has very good advice about handling the file itself. I would also look at references you may be hanging on to in your web processing. Do store anything in Session State or Application State? If so, trace those carefully to make sure they don't in turn point to the page or somethign else involved with handling your files.

I mention this becuase we had a nasty 'leak' a few years ago caused by placing an object in application state. Turns out that object subscribed to page events, and since the object never died neither did all the pages! oops

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