下载视频文件时内存不足异常
Out Of Memory Exception when downloading a video file
我们目前从 Twilio 下载视频,下载视频后我们将视频保存在 AWS S3
中。但是,在部署到生产环境时,我目前得到:
Exception of type 'System.OutOfMemoryException' was thrown.
尝试下载视频时,
我检查了 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");
任何人都可以为此推荐解决方案/修复方法吗?
正如您可能已经猜到的那样,如果视频很大,将整个视频保存在内存中是个坏主意。
尽管 180mb 看起来并不多,但它仍然需要一个连贯的内存区域用于 LOH 中的内存区域。使用一些按块写入磁盘的方法可能会更好。为了提高性能和避免 LOH 中的内存碎片,您也可以顺便使用 RecyclableMemoryStream
,但这对您当前的系统没有任何作用。
通常你应该避免这种设计。保存到磁盘(使用内存缓冲)并信任小文件的文件系统缓存,或者尝试直接将下游馈送到上游。
理论上,在 S3(7GB 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()
.
的使用
感谢 mjwills 发现 responseBody
也可以放入 using()
块中,从而节省显式 Close
.
前进
即使有了这些修复,您可能仍希望利用流式资源的最佳做法,而不是一次加载所有资源,尤其是在不需要一次将所有资源加载到内存中的情况下。只需为源创建一个读取流,为目标创建一个写入流。那就简单的一次读写块了。
我们目前从 Twilio 下载视频,下载视频后我们将视频保存在 AWS S3
中。但是,在部署到生产环境时,我目前得到:
Exception of type 'System.OutOfMemoryException' was thrown.
尝试下载视频时,
我检查了 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");
任何人都可以为此推荐解决方案/修复方法吗?
正如您可能已经猜到的那样,如果视频很大,将整个视频保存在内存中是个坏主意。
尽管 180mb 看起来并不多,但它仍然需要一个连贯的内存区域用于 LOH 中的内存区域。使用一些按块写入磁盘的方法可能会更好。为了提高性能和避免 LOH 中的内存碎片,您也可以顺便使用 RecyclableMemoryStream
,但这对您当前的系统没有任何作用。
通常你应该避免这种设计。保存到磁盘(使用内存缓冲)并信任小文件的文件系统缓存,或者尝试直接将下游馈送到上游。
理论上,在 S3(7GB 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()
.
感谢 mjwills 发现 responseBody
也可以放入 using()
块中,从而节省显式 Close
.
前进
即使有了这些修复,您可能仍希望利用流式资源的最佳做法,而不是一次加载所有资源,尤其是在不需要一次将所有资源加载到内存中的情况下。只需为源创建一个读取流,为目标创建一个写入流。那就简单的一次读写块了。