2015-08-14 78 views
4

我正在調查用戶上傳文件的項目中可能發生的內存泄漏問題。這些文件通常是用於其他軟件的.zip或.exe壓縮文件。這些文件的平均大小爲80MBASP.NET MVC/WEB API文件上傳:上傳後沒有釋放內存

有一個MVC應用程序,它具有上載文件的界面(View)。該視圖向控制器內的操作發送POST請求。此控制器操作使用與此類似的MultipartFormDataContent獲取文件:Sending binary data along with a REST API request和此:WEB API FILE UPLOAD, SINGLE OR MULTIPLE FILES

在動作中,我得到該文件並將其轉換爲字節數組。轉換後,我用byte []數組發送一個post請求到我的API。

這裏是MVC應用程序代碼,不會說:

[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,因爲我們需要在(業務需求/規則)之前發送和驗證一些數據。經過研究,我已經使用了這種方法,但內存泄漏問題與我有關。

我錯過了什麼嗎?垃圾收集器應該立即收集內存嗎?在所有可丟棄的對象中,我使用「using」語法,但它沒有幫助。

我也很好奇這種方法上傳文件。我應該以不同的方式做事嗎?

只是爲了澄清,API與MVC應用程序(每個託管在IIS中的獨立Web站點上)分開,並且全部都在C#中。

回答

2

1.垃圾收集器應該立即收集內存嗎?

垃圾回收器不會立即釋放內存,因爲這是一項耗時的操作。當垃圾收集發生時,所有應用程序的管理線程都會暫停。這引入了不需要的延遲。所以,垃圾收集器只能根據複雜的算法偶爾發揮作用。

2.在所有可丟棄的對象中,我使用「using」語法,但它沒有幫助。

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

然而,如果你需要釋放大量的內存,這將是在時間上昂貴的操作。