【问题标题】:Out Of Memory Exception when downloading a video file下载视频文件时出现内存不足异常
【发布时间】:2020-07-07 00:07:53
【问题描述】:

我们目前从 Twilio 下载视频,下载视频后,我们会将视频保存在 AWS S3。但是,在部署到生产环境时,我目前得到:

引发了“System.OutOfMemoryException”类型的异常。

尝试下载视频时,

我检查了 Twilio 中的视频大小,它们相对较小,为 172,912kb

我已将 S3 上的实例升级为大型实例,因为我认为这将是一个问题,因为在它们很小之前。

但问题依旧存在,失败的代码块如下:

 var request = (HttpWebRequest)WebRequest.Create($"{resource.Url}/Media");
 request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(_twilioOptions.ApiKey + ":" + _twilioOptions.ApiSecret)));
 request.AllowAutoRedirect = true;

 var responseBody = (request.GetResponseAsync().Result).GetResponseStream();

 byte[] tempBuffer = new byte[8192];
 MemoryStream ms = new MemoryStream();
 int read;
 while ((read = responseBody.Read(tempBuffer, 0, tempBuffer.Length)) > 0)
 {
    ms.Write(tempBuffer, 0, read);
 }

 var result = ms;
    
 var fileName = $"Conversations/{conversationId}/Video-{DateTime.UtcNow:yyyyMMdd-hhmmss}-{compositionSid}.{resource.Format}";
 var uploadedFile = await _fileUploader.UploadFile(result, fileName, _s3Options.SecureBucket, "video/mp4");

谁能推荐一个解决方案/解决这个问题?

【问题讨论】:

  • 文件已经在流中 (GetResponseStream)。为什么要将其复制到 new 流(MemoryStream)?
  • 整理你的记忆。它会消耗更少的内存区域。
  • @Arphile 这是什么意思?
  • 你试过我的建议了吗@CodeRatchet?

标签: c# memorystream


【解决方案1】:

理论上,在 S37GB RAM 计划)上一次将大约 170MB 的文件下载到内存中应该没有问题。但是,您不会 处置或关闭您的任何资源。根据周围的代码,这很可能是未释放非托管资源的问题,并可能导致一段时间内内存不足。

现在我们可以显式调用Close()Dispose(),但将相关代码封装在using() 块中会更容易,如下所示:

 
 byte[] tempBuffer = new byte[8192];
 using (var responseBody = (request.GetResponseAsync().Result).GetResponseStream()) 
 using (var ms = new MemoryStream()) // <------- using block, will call Dispose() 
 {
     int read;
     while ((read = responseBody.Read(tempBuffer, 0, tempBuffer.Length)) > 0)
     {
        ms.Write(tempBuffer, 0, read);
     }

     var result = ms;
    
     var fileName = $"Conversations/{conversationId}/Video-{DateTime.UtcNow:yyyyMMdd-hhmmss}-{compositionSid}.{resource.Format}";
     var uploadedFile = await _fileUploader.UploadFile(result, fileName, 
                                                      _s3Options.SecureBucket, "video/mp4");
}


您的代码类似于this MSDN 示例,但是您会注意到Close() 的使用。

Tell me more

感谢 mjwills 发现 responseBody 也可以放入 using() 块中,从而保存明确的 Close

继续前进

即使进行了这些修复,您也可能希望利用流式资源的最佳做法,而不是一次加载所有资源,尤其是在不需要一次将其全部加载到内存中的情况下。只需为源创建一个读取流并为目标创建一个写入流。那么这只是一次读取和写入块的问题。

【讨论】:

    【解决方案2】:

    您可能已经猜到了,如果视频很大,最好将整个视频保存在内存中。

    尽管 180mb 看起来并不多,但 LOH 中仍然需要一个连贯的内存区域。使用某些将块写入磁盘的方法可能会更好。为了提高性能并避免 LOH 中的内存碎片,您也可以顺便使用RecyclableMemoryStream,但这对您当前的系统没有任何作用。

    通常你应该避免这种设计。要么保存到磁盘(带有内存缓冲)并信任小文件的文件系统缓存,要么尝试将下游直接馈送到上游。

    【讨论】:

    • “尽管 180mb 看起来并不多” - 正如 OP 所说,他正在使用 "S3" 服务计划,该计划包括 7GB 的 RAM
    • 但它是关于拥有足够大的非碎片内存块......
    猜你喜欢
    • 2012-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多