C# 在IIS上执行一个进程会使RAM上升得非常快

C# 在IIS上执行一个进程会使RAM上升得非常快,c#,.net,asp.net-mvc,iis,memory-management,C#,.net,Asp.net Mvc,Iis,Memory Management,我在Windows10Pro上构建了一个ASP.NETMVCAPI托管在IIS上(Azure-4GB RAM上的虚拟机,2CPU)。在其中,我调用了一个.exe(),希望将HTML页面转换为图像并保存在本地。一切正常,只是我注意到在一些API调用之后,RAM变得疯狂,在使用Task Manager调查流程时,我看到了一个名为IIS Worker process的流程,该流程在每次调用API时都会添加更多RAM。当然,我将我的System.Diagnostics.Process实例使用情况包装在一

我在Windows10Pro上构建了一个ASP.NETMVCAPI托管在IIS上(Azure-4GB RAM上的虚拟机,2CPU)。在其中,我调用了一个.exe(),希望将HTML页面转换为图像并保存在本地。一切正常,只是我注意到在一些API调用之后,RAM变得疯狂,在使用Task Manager调查流程时,我看到了一个名为IIS Worker process的流程,该流程在每次调用API时都会添加更多RAM。当然,我将我的System.Diagnostics.Process实例使用情况包装在一个using语句中以进行处理,因为实现了IDisposable,但它仍然会消耗越来越多的RAM,过了一段时间服务器就会变得迟钝和无响应(毕竟它只有4GB的RAM)。我注意到在几分钟后(可能是10-15-20分钟),这个IIS工作进程在RAM使用方面平静下来。。。这是我的代码,非常简单:

  • 获取base64编码的url
  • 解码
  • 使用wkhtmltoimage.exe将其转换为图像
  • 在本地保存它
  • 读取字节数组
  • 使用图像在Azure中创建blob
  • 返回带有url的json

    public async Task<ActionResult> Index(string url)
    {
        object oJSON = new { url = string.Empty };
    
        if (!string.IsNullOrEmpty(value: url))
        {
            try
            {
                byte[] EncodedData = Convert.FromBase64String(s: url);
                string DecodedURL = Encoding.UTF8.GetString(bytes: EncodedData);
    
                using (Process proc = new Process())
                {
                    proc.StartInfo.FileName = wkhtmltopdfExecutablePath;
                    proc.StartInfo.Arguments = $"--encoding utf-8 \"{DecodedURL}\" {LocalImageFilePath}";
                    proc.Start();
                    proc.WaitForExit();
    
                    oJSON = new { procStatusCode = proc.ExitCode };
                }
    
                if (System.IO.File.Exists(path: LocalImageFilePath))
                {
                    byte[] pngBytes = System.IO.File.ReadAllBytes(path: LocalImageFilePath);
    
                    System.IO.File.Delete(path: LocalImageFilePath);
    
                    string ImageURL = await CreateBlob(blobName: $"{BlobName}.png", data: pngBytes);
    
                    oJSON = new { url = ImageURL };
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(value: ex);
            }
        }
    
        return Json(data: oJSON, behavior: JsonRequestBehavior.AllowGet);
    }
    
    private async Task<string> CreateBlob(string blobName, byte[] data)
    {
        string ConnectionString = "DefaultEndpointsProtocol=https;AccountName=" + AzureStorrageAccountName + ";AccountKey=" + AzureStorageAccessKey + ";EndpointSuffix=core.windows.net";
        CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse(connectionString: ConnectionString);
        CloudBlobClient cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient();
        CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference(containerName: AzureBlobContainer);
    
        await cloudBlobContainer.CreateIfNotExistsAsync();
    
        BlobContainerPermissions blobContainerPermissions = await cloudBlobContainer.GetPermissionsAsync();
        blobContainerPermissions.PublicAccess = BlobContainerPublicAccessType.Container;
    
        await cloudBlobContainer.SetPermissionsAsync(permissions: blobContainerPermissions);
    
        CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(blobName: blobName);
        cloudBlockBlob.Properties.ContentType = "image/png";
    
        using (Stream stream = new MemoryStream(buffer: data))
        {
            await cloudBlockBlob.UploadFromStreamAsync(source: stream);
        }
    
        return cloudBlockBlob.Uri.AbsoluteUri;
    }
    

    造成您痛苦的最可能原因是分配大字节数组:

    byte[] pngBytes = System.IO.File.ReadAllBytes(path: LocalImageFilePath);
    
    要尝试并鼓励GC更频繁地收集大型对象堆,最简单的更改是在方法末尾将
    GCSettings.LargeObjectHeapCompactionMode
    设置为
    CompactOnce
    。这可能会有帮助

    但是,更好的办法是完全不需要使用大型阵列。为此,请更改:

    private async Task<string> CreateBlob(string blobName, byte[] data)
    

    在调用者中,您需要停止使用
    ReadAllBytes
    ,而是使用
    FileStream
    来读取文件。

    唯一会导致内存使用量增加的是
    CreateBlob()
    ,它未显示。您需要分析您的代码和内存使用情况,这不是我们可以轻松做到的。启动进程不会导致启动进程的内存增加,因此问题的一部分是转移注意力。我使用CreatBlob方法更新了问题这也可能是个问题。(如果文件较大,则更应如此)
    byte[]pngBytes=System.IO.file.ReadAllBytes(路径:LocalImageFilePath)如果这种情况偶尔发生,进程可能会收到越来越多的内存,即使它可能没有被使用。正如其他人所说:评测你的应用程序。如果你使用
    FileStream
    并将其传递给
    UploadFromStreamAsync
    ,性能会提高吗?注意,这将要求更改<代码>字节[]/Case>参数<代码> CreateBlob <代码>作为流,并停止使用<代码> RealAlpByth。您可以考虑卸载所有的工作,无论如何,IMO并不真正适用于请求/响应周期。您应该能够在不等待进程运行的情况下知道图像url,这样我就可以在队列中放置一条消息,并让您的进程在Azure函数或类似函数中运行。
    private async Task<string> CreateBlob(string blobName, byte[] data)
    
    private async Task<string> CreateBlob(string blobName, FileStream data)
    
    await cloudBlockBlob.UploadFromStreamAsync(source: data);