昨天在园子里提了一个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的累积,累积 |