昨天在園子里提了一個EFcore多線程查詢導致服務內存飆升的問題,現在依然還沒有找到解決辦法,今天把問題詳細的陳述一遍。
剛開始發現問題是公司一個服務的內存飈到了7個G,系統直接殺死了這個進程,后面發現問題是隨着高並發的訪問,里面的EFcore進行大量數據查詢,在訪問結束后內存沒有釋放干凈,內存不斷的堆積,服務直接就崩了。然后我在本地用例還原了這個過程,大致如下:
GRPC服務端:
(一開始懷疑是DatabaseContext注入的問題,用了AddScoped;AddTransient;后者直接用using都沒有解決問題,所以不是這個問題)
在GRPC里一個方法SayHello查詢一張表的數據(數據大概1w多)
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
var d = efoscoreContext.Project.Where(w => w.ProjectId > 15).Select(s => new
{
*******
}).ToList();
return Task.FromResult(new HelloReply
{
Message = "Hello " + request.Name
});
}
客戶端:
每三秒調一次Grpc的SayHello方法,后面發現GRPC服務里確實釋放不完內存,隨着訪問的次數的增加,內存會堆積到一個很高的值,如果內存抗不住就崩了
while (true)
{
for (int i = 1; i < 40; i++)
{
reply = client.SayHello(new FirstGrpc.HelloRequest() { Name = "" });
}
Thread.Sleep(TimeSpan.FromSeconds(3));
}
過程會進行GC,但是還會剩下大量的內存沒GC完,而且會累積到很高。
后面思考,GRPC服務以線程池的方式響應請求,會不會是Efcore在多線程里出現了問題的原因
后面我用控制台做了一個測試,發現問題確實是這樣,Efocore在多線程查詢,線程結束后,內存並沒有回收完
多線程Efocore查詢用例:
先順序執行:
while (true)
{
for (int i = 1; i < 100; i++)
{
ProjectQuery()//執行efocore查詢
;
}
Thread.Sleep(TimeSpan.FromSeconds(5));
}
順序執行沒有任何問題,內存占用是很正常的也就一直100多M
多線程執行:
while (true)
{
for (int i = 1; i < 100; i++)
{
var t = new Task(() => ProjectQuery()//執行efocore查詢
);
t.Start();
}
Thread.Sleep(TimeSpan.FromSeconds(5));
}
多線程執行一上來就飈到了2G,后面會剩下1.2g一直在那里,再后面就會1-4M的累積,累積