【问题标题】:ASP.NET MVC/WEB API File Upload: Memory not being deallocated after uploadASP.NET MVC/WEB API 文件上传:上传后内存未释放
【发布时间】:2015-11-08 03:17:18
【问题描述】:

我正在调查用户上传文件的项目中可能存在的内存泄漏问题。这些文件通常是用于其他软件的 .zip 或 .exe 压缩文件。文件的平均大小为 80MB

有一个 MVC 应用程序具有上传文件的接口(视图)。此视图向控制器中的操作发送 POST 请求。此控制器操作使用类似于此的 MultipartFormDataContent 获取文件:Sending binary data along with a REST API request 和此:WEB API FILE UPLOAD, SINGLE OR MULTIPLE FILES

在操作中,我获取文件并将其转换为字节数组。转换后,我使用 byte[] 数组向我的 API 发送一个发布请求。

这是执行此操作的 MVC APP 代码:

[HttpPost]
    public async Task<ActionResult> Create(ReaderCreateViewModel model)
    {
        HttpPostedFileBase file = Request.Files["Upload"];

        string fileName = file.FileName;

        using (var client = new HttpClient())
        {
            using (var content = new MultipartFormDataContent())
            {                   
                using (var binaryReader = new BinaryReader(file.InputStream))
                {
                    model.File = binaryReader.ReadBytes(file.ContentLength);
                }

                var fileContent = new ByteArrayContent(model.File);
                fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
                {
                    FileName = file.FileName
                };
                content.Add(fileContent);

                var requestUri = "http://localhost:52970/api/upload";
                HttpResponseMessage response = client.PostAsync(requestUri, content).Result;

                if (response.IsSuccessStatusCode)
                {                       
                    return RedirectToAction("Index");
                }
            }
        }

        return View("Index", model);
    }

在调查了使用以下几种内存工具后:Best Practices No. 5: Detecting .NET application memory leaks 我发现在此行将文件转换为字节数组后:

using (var binaryReader = new BinaryReader(file.InputStream))
{
      model.File = binaryReader.ReadBytes(file.ContentLength);
}

内存使用量从 70MB + 或 - 增加到 175mb + 或 - 并且即使在发送并完成请求之后,内存也不会被释放。如果我继续上传文件,内存只会不断增加,直到服务器完全关闭。

我们无法将文件直接从多部分表单发送到 API,因为我们需要在之前发送和验证一些数据(业务需求/规则)。经过研究,我采用了这种方法,但内存泄漏问题与我有关。

我错过了什么吗?垃圾收集器应该立即收集内存吗?在所有一次性物品中,我都使用“使用”语法,但它没有帮助。

我也很好奇这种上传文件的方法。我应该以不同的方式做事吗?

为了澄清,API 与 MVC 应用程序是分开的(每个都托管在 IIS 中的单独网站上),并且全部使用 C#。

【问题讨论】:

    标签: c# asp.net-mvc file-upload asp.net-web-api memory-leaks


    【解决方案1】:

    1.垃圾收集器是否应该立即收集内存?

    垃圾收集器不会立即释放内存,因为这是一个耗时的操作。发生垃圾回收时,应用程序的所有托管线程都将暂停。这引入了不需要的延迟。因此,垃圾收集器只是偶尔根据复杂的算法进行操作。

    2。在所有一次性物品中,我都使用了“使用”语法,但它没有帮助。

    using 语句处理供应有限的非托管资源 (通常与 IO 相关,如文件句柄、数据库和网络连接)。因此,该语句不会影响垃圾回收。

    3.我错过了什么吗?

    使用ByteArrayContent 包装后,您似乎不需要原始字节数组。包装后不清理model.File,数组最终会被传递到索引视图。

    我会替换:

    using(var binaryReader = new BinaryReader(file.InputStream)) {
        model.File = binaryReader.ReadBytes(file.ContentLength);
    }
    var fileContent = new ByteArrayContent(model.File);
    

    与:

    ByteArrayContent fileContent = null;
    using(var binaryReader = new BinaryReader(file.InputStream)) {
        fileContent = new ByteArrayContent(binaryReader.ReadBytes(file.ContentLength));
    }
    

    避免需要明确地清理model.File

    4.如果我继续上传文件,内存只会不断增加,直到服务器完全关闭。

    如果您的文件平均为 80MB,则它们最终位于大型对象堆中。堆不会自动压缩,通常不会被垃圾收集。在您的情况下,大对象堆看起来会无限增长(这可能会发生)。

    如果您使用(或可以升级到).NET 4.5.1 或更高版本,您可以通过设置强制压缩大对象堆:

    System.Runtime.GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
    

    每次您想在下一次完全垃圾回收时安排大型对象堆压缩时,都需要调用这行代码。

    你也可以通过调用来强制立即压缩:

    System.Runtime.GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
    System.GC.Collect();
    

    但是,如果您需要释放大量内存,这将是一项耗时的操作。

    【讨论】:

      猜你喜欢
      • 2012-07-27
      • 2020-07-10
      • 2014-05-15
      • 2015-08-21
      • 1970-01-01
      • 2016-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多