多线程控制台应用程序中的 EF 核心内存泄漏
EF core Memory leak in multithreaded console application
我创建了一个小型控制台应用程序,我在其中模拟了在 Azure 服务总线消息传递应用程序中使用 EF 核心时遇到的内存泄漏问题。
设置如下:
我继承自MyContext中的DbContext
public class MyContext : DbContext
{
private readonly Assembly configurationAssembly;
public MyContext(Assembly configurationAssembly, DbContextOptions dbContextOptions) : base(dbContextOptions)
{
this.configurationAssembly = configurationAssembly;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(this.configurationAssembly);
}
}
我有一个 POCO class:
public class Job
{
public int Id { get; set; }
public string Applicationcode { get; set; }
}
还有一个 EntityTypeConfiguration:
public class JobConfig : IEntityTypeConfiguration<Job>
{
public void Configure(EntityTypeBuilder<Job> builder)
{
builder.ToTable("Job_OP");
}
}
MyContext 在 OnModelCreating 重写 (ApplyConfigurationFromAssembly) 中使用此配置
最后在程序中我有以下代码:
class Program
{
private static IContainer container;
static void Main(string[] args)
{
var builder = new ContainerBuilder(); //Autofac dependency injection
var dbContextOptionsBuilder = new DbContextOptionsBuilder().UseSqlServer("Data Source=localhost;Initial Catalog=TestEFThreads;Integrated Security=True");
builder.Register(ctx => new MyContext(
Assembly.Load("TestEFThreads"),
dbContextOptionsBuilder.Options));
//I tried to use different lifetime scopes here, but EF doesn't like using the same dbContext concurrently in different threads.
//Currently each resolve instantiates a new MyContext
container = builder.Build();
var input = ShowMenu();
var nrOfSimulatedMessages = 0;
while (!string.Equals(input, "q", StringComparison.OrdinalIgnoreCase))
{
try
{
nrOfSimulatedMessages = Convert.ToInt32(input);
PerformSimulation(maxThreads: 10, nrOfSimulatedMessages);
}
catch (Exception e)
{
Console.WriteLine($"Incorrect input or exception, {e}");
}
input = ShowMenu().ToLower();
}
}
static string ShowMenu()
{
Console.WriteLine("Enter number of simulated messages or q to quit");
return Console.ReadLine();
}
private static void PerformSimulation(int maxThreads, int nrOfSimulatedMessages)
{
var messageCount = 0;
var tasks = new Task[maxThreads];
while (messageCount < nrOfSimulatedMessages)
{
for (int i = 0; i < maxThreads && messageCount < nrOfSimulatedMessages; i++)
{
Console.WriteLine($"Creating thread {i} for message {messageCount}");
tasks[i] = new Task(StartProcessing);
tasks[i].Start();
messageCount++;
}
Console.WriteLine("Waiting for all threads to finish");
Task.WaitAll(tasks.Where(a=>a !=null).ToArray());
}
}
private static void StartProcessing()
{
using (var lifeTime = container.BeginLifetimeScope()) //does not help
{
var myContext = container.Resolve<MyContext>();
var job = myContext.Set<Job>().SingleOrDefault(a => a.Id == 1);
Console.WriteLine($"Found job with applicationCode {job.Applicationcode}");
myContext.Dispose(); //memory leak even with dispose
}
}
}
所以此应用程序从数据库中进行简单读取,并执行指定次数,每次都在一个单独的线程中。
当我 运行 它大约 50000 'messages' 时,内存不断增加,如诊断中所示。
它似乎没有被垃圾收集。
有人知道如何修复此内存泄漏吗?
更新:
它似乎与 Autofac 依赖注入有关。
如果我只是使用新的 MyContext iso 解析它,内存使用就很好。
private static void StartProcessing()
{
var myContext = new MyContext(Assembly.Load("TestEFThreads"), dbContextOptionsBuilder.Options);
var job = myContext.Set<Job>().SingleOrDefault(a => a.Id == 1);
Console.WriteLine($"Found job with applicationCode {job.Applicationcode}");
}
但是我确实需要使用依赖注入。
使用 ExternallyOwned 似乎可以解决问题。
builder.Register(ctx => new MyContext(
Assembly.Load("TestEFThreads"),
dbContextOptionsBuilder.Options)).ExternallyOwned();
我创建了一个小型控制台应用程序,我在其中模拟了在 Azure 服务总线消息传递应用程序中使用 EF 核心时遇到的内存泄漏问题。
设置如下:
我继承自MyContext中的DbContext
public class MyContext : DbContext
{
private readonly Assembly configurationAssembly;
public MyContext(Assembly configurationAssembly, DbContextOptions dbContextOptions) : base(dbContextOptions)
{
this.configurationAssembly = configurationAssembly;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(this.configurationAssembly);
}
}
我有一个 POCO class:
public class Job
{
public int Id { get; set; }
public string Applicationcode { get; set; }
}
还有一个 EntityTypeConfiguration:
public class JobConfig : IEntityTypeConfiguration<Job>
{
public void Configure(EntityTypeBuilder<Job> builder)
{
builder.ToTable("Job_OP");
}
}
MyContext 在 OnModelCreating 重写 (ApplyConfigurationFromAssembly) 中使用此配置
最后在程序中我有以下代码:
class Program
{
private static IContainer container;
static void Main(string[] args)
{
var builder = new ContainerBuilder(); //Autofac dependency injection
var dbContextOptionsBuilder = new DbContextOptionsBuilder().UseSqlServer("Data Source=localhost;Initial Catalog=TestEFThreads;Integrated Security=True");
builder.Register(ctx => new MyContext(
Assembly.Load("TestEFThreads"),
dbContextOptionsBuilder.Options));
//I tried to use different lifetime scopes here, but EF doesn't like using the same dbContext concurrently in different threads.
//Currently each resolve instantiates a new MyContext
container = builder.Build();
var input = ShowMenu();
var nrOfSimulatedMessages = 0;
while (!string.Equals(input, "q", StringComparison.OrdinalIgnoreCase))
{
try
{
nrOfSimulatedMessages = Convert.ToInt32(input);
PerformSimulation(maxThreads: 10, nrOfSimulatedMessages);
}
catch (Exception e)
{
Console.WriteLine($"Incorrect input or exception, {e}");
}
input = ShowMenu().ToLower();
}
}
static string ShowMenu()
{
Console.WriteLine("Enter number of simulated messages or q to quit");
return Console.ReadLine();
}
private static void PerformSimulation(int maxThreads, int nrOfSimulatedMessages)
{
var messageCount = 0;
var tasks = new Task[maxThreads];
while (messageCount < nrOfSimulatedMessages)
{
for (int i = 0; i < maxThreads && messageCount < nrOfSimulatedMessages; i++)
{
Console.WriteLine($"Creating thread {i} for message {messageCount}");
tasks[i] = new Task(StartProcessing);
tasks[i].Start();
messageCount++;
}
Console.WriteLine("Waiting for all threads to finish");
Task.WaitAll(tasks.Where(a=>a !=null).ToArray());
}
}
private static void StartProcessing()
{
using (var lifeTime = container.BeginLifetimeScope()) //does not help
{
var myContext = container.Resolve<MyContext>();
var job = myContext.Set<Job>().SingleOrDefault(a => a.Id == 1);
Console.WriteLine($"Found job with applicationCode {job.Applicationcode}");
myContext.Dispose(); //memory leak even with dispose
}
}
}
所以此应用程序从数据库中进行简单读取,并执行指定次数,每次都在一个单独的线程中。
当我 运行 它大约 50000 'messages' 时,内存不断增加,如诊断中所示。 它似乎没有被垃圾收集。
有人知道如何修复此内存泄漏吗?
更新: 它似乎与 Autofac 依赖注入有关。 如果我只是使用新的 MyContext iso 解析它,内存使用就很好。
private static void StartProcessing()
{
var myContext = new MyContext(Assembly.Load("TestEFThreads"), dbContextOptionsBuilder.Options);
var job = myContext.Set<Job>().SingleOrDefault(a => a.Id == 1);
Console.WriteLine($"Found job with applicationCode {job.Applicationcode}");
}
但是我确实需要使用依赖注入。
使用 ExternallyOwned 似乎可以解决问题。
builder.Register(ctx => new MyContext(
Assembly.Load("TestEFThreads"),
dbContextOptionsBuilder.Options)).ExternallyOwned();