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

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

在观看了一段时间后,我注意到,由于这些PDF在浏览器窗口中呈现,直到文件完全下载,它才会从内存中释放。 这是有道理的,但我可以看到这有点像我的问题。 如果我有几个人加载文件,平均(没有文件下载)内存使用量为385,000 kb。它可以轻松达到900,000-1,100,000 KB,这是应用程序池的限制。

我不是在寻找一个确切的答案,而是更像是一个方向,因为我完全没有想法。

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

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

伪示例:

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(); } } 

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

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

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

 #region WriteResponse(HttpContext context, Guid id) ///  /// Writes the content for a media resource with the specified  /// to the response stream using the appropriate content type and length. ///  /// The  to write content to. /// The unique identifier assigned to the media resource. 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) ///  /// Writes the content for a media resource to the response stream using the supplied . ///  /// The  to write content to. /// The  to extract information from. 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) ///  /// Writes the content for a media resource to the response stream using the /// specified reader, content type and content length. ///  /// The  to write content to. /// The  to extract information from. /// The content type of the media. /// The content length of the media. 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 

Oppositional对处理文件本身有很好的建议。 我还会查看您在Web处理中可能会依赖的引用。 在会话状态或应用程序状态中存储任何内容? 如果是这样,请仔细跟踪这些内容,以确保它们不会指向页面或处理文件所涉及的其他任何其他内容。

我提到这一点,因为几年前我们有一个令人讨厌的“漏洞”,这是因为一个物体处于应用状态。 事实certificate,该对象订阅了页面事件,并且由于该对象从未死亡,所以所有页面都没有! 哎呀