如何分析EFCore引发的内存泄漏
調查實體框架核心中的內存泄漏
不要讓內存泄漏成為洪水術語“內存泄漏”和“ .NET應用程序”不是經常一起使用。但是,我們最近在一個.NET Core Web應用程序中出現了一系列內存不足異常。事實證明,此問題是由Entity Framework Core中的行為更改引起的,盡管最終的解決方案非常簡單,但實現該目標的過程既充滿挑戰又有趣。
該系統本身托管在Azure中,由Angular SPA前端和后端的.NET Core API組成,使用Entity Framework Core與Azure SQL數據庫進行通信。作為專門從事.NET開發的軟件咨詢公司,我們之前已經編寫了許多類似的應用程序。因此,內存不足崩潰[1]是無法預料的,因此我們立即知道這是需要認真對待的事情。使用Azure門戶中的指標,我們可以看到內存使用率穩步上升,然后突然下降:此下降是應用程序崩潰。
修復之前?因此,我們花了一些時間進行調查并逐步進行更改,以解決看似經典的內存泄漏問題。.NET泄漏的常見原因是未正確處理某些問題,在我們的案例中很可能是EF Core數據庫上下文。因此,我們遍歷了源代碼,以尋找可能無法處理上下文的潛在原因。這變成了空白。
我們將Entity Framework Core升級到了最新版本,因為最近的更新包括各種內存泄漏的修復程序和總體效率的提高。
我們還在使用的Application Insights版本中發現了可能的內存泄漏(請參閱https://github.com/microsoft/ApplicationInsights-dotnet/issues/594),因此我們也對該軟件包進行了升級。
這些都不能解決問題,因此我們解剖了從Azure應用服務中獲取的內存轉儲(請參閱https://blogs.msdn.microsoft.com/jpsanders/2017/02/02/how-to-get-a-full-memory-dump-in-azure-app-services/)。
我們注意到,絕大多數托管內存最終都由MemoryCache類使用。進一步深入研究表明,大多數緩存數據都是原始SQL查詢的形式。我們看到大量的根本上是同一查詢的事件被多次緩存,并且參數本身被硬編碼在查詢中而不是被參數化。
例如,與其像這樣緩存查詢:
SELECT TOP (1) UserId, FirstName, LastName, EmailAddress FROM Users WHERE UserId = @param_1我們發現這樣的多個查詢:
SELECT TOP (1) UserId, FirstName, LastName, EmailAddress FROM Users WHERE UserId = 5因此,我們進行了一些搜索,尋找可能與之相關的EF核心問題,并遇到了這個問題:https[2]?:?//github.com/aspnet/EntityFrameworkCore/issues/10535[3]。
關于這個問題的主題指出了這個問題:我們正在建立一個動態表達式樹,并使用它Expressions.Expression.Constant?來為where子句提供參數。使用常量表達式意味著Entity Framework Core不會參數化SQL查詢,并且是Entity Framework 6的行為更改。
我們到處都使用這個表達式樹,通過它的ID來獲取某些東西,這就是為什么它是一個很大的問題。
因此,這就是我們所做的更改:
// Before var param = Expressions.Expression.Parameter(typeof(T)); Expression = Expressions.Expression.Lambda<Func<T, bool>>(Expressions.Expression.Call(Expressions.Expression.Constant(valuesToFilter),"Contains",Type.EmptyTypes,Expressions.Expression.Property(param, propertyName)),param); // After var param = Expressions.Expression.Parameter(typeof(T)); // This is what we added Expression<Func<List<int>>> valuesToFilterLambda = () => valuesToFilter; Expression = Expressions.Expression.Lambda<Func<T, bool>>(Expressions.Expression.Call(valuesToFilterLambda.Body,"Contains",Type.EmptyTypes,Expressions.Expression.Property(param, propertyName)),param);使用lambda表達式獲取表達式主體會使Entity Framework Core[4]對SQL查詢進行參數化,因此僅緩存它的一個實例。
這是包括修訂版本在內的一段時間內的內存使用情況。該版本以紅色標記,您可以看到差異很大。穩定的內存使用量從未超過200MB,而不斷攀升至超過1GB,然后發生崩潰。
修復后最初進行調查時,真正的解決方案不是我們要注意的事情,而是通過檢查內存轉儲并遵循證據我們最終到達那里。
從此調查中可以汲取的教訓是:
?內存轉儲不會說謊-如果內存泄漏,請先查看證據。?微軟已經開放了EF Core的源代碼,所有問題在那里所有人都可以看到,對有需求的開發者來說非常方便。?簡單的代碼更改(在這種情況下為一行)可能會產生巨大的影響。
References
[1]?,內存不足崩潰:?https://dzone.com/articles/what-causes-outofmemoryerror
[2]?https:?https://github.com/aspnet/EntityFrameworkCore/issues/10535
[3]?//github.com/aspnet/EntityFrameworkCore/issues/10535:?https://github.com/aspnet/EntityFrameworkCore/issues/10535
[4]?Entity Framework Core:?https://dzone.com/articles/entity-framework-core-30-and-sql-server-2019-perfo
總結
以上是生活随笔為你收集整理的如何分析EFCore引发的内存泄漏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: async,await执行流看不懂?看完
- 下一篇: 微服务统计,分析,图表,监控, 分布式追