Quartz.Net任务执行时内存持续增长致OutOfMemoryException求助
排查Quartz.Net结合PostgreSQL的内存泄漏问题
问题描述
在.NET API项目中使用Quartz.Net调度任务,结合PostgreSQL存储任务和触发器,任务的创建、删除、执行均正常,但程序运行时内存占用持续增长,一段时间后抛出OutOfMemoryException并崩溃。通过Rider监控发现存在未释放的服务或类,尝试自定义JobFactory、将任务/JobFactory/IScheduler注册为单例等方案后仍未解决。
环境配置
NuGet包
<PackageReference Include="Quartz" Version="3.13.1" /> <PackageReference Include="Quartz.AspNetCore" Version="3.13.1" /> <PackageReference Include="Quartz.Extensions.DependencyInjection" Version="3.13.1" /> <PackageReference Include="Quartz.Serialization.Json" Version="3.13.1" /> <PackageReference Include="Npgsql" Version="8.0.6" />
appsettings.json配置
"ConnectionStrings": { "DefaultConnection": "User ID=postgres;Password=postgres;Server=localhost;Port=5432;Database=postgre_db" }, "Quartz": { "quartz.scheduler.instanceName": "default", "quartz.jobStore.type": "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz", "quartz.jobStore.driverDelegateType": "Quartz.Impl.AdoJobStore.PostgreSQLDelegate, Quartz", "quartz.jobStore.tablePrefix": "QRTZ_", "quartz.jobStore.dataSource": "default", "quartz.dataSource.default.connectionStringName": "DefaultConnection", "quartz.dataSource.default.provider": "Npgsql", "quartz.jobStore.useProperties": "true", "quartz.serializer.type": "newtonsoft" }
Program.cs配置
// Read configurations in appsettings.json builder.Services.Configure<QuartzOptions>(builder.Configuration.GetSection("Quartz")); // Use Quartz builder.Services.AddQuartz(configurator => { configurator.UsePersistentStore(options => { options.UsePostgres(builder.Configuration.GetConnectionString("DefaultConnection")!); options.UseNewtonsoftJsonSerializer(); }); // Create a job and trigger JobKey jobFromProgram = new("JobFromProgram"); configurator.AddJob<SimpleJob>(jobFromProgram); configurator.AddTrigger(trigger => trigger.ForJob(jobFromProgram) .WithIdentity("JobFromProgramTrigger") .WithCronSchedule("*/5 * * ? * *")); // Run every 5 seconds }); builder.Services.AddQuartzHostedService(options => { options.WaitForJobsToComplete = true; });
任务类
public class SimpleJob(ILogger<SimpleJob> logger) : IJob { public Task Execute(IJobExecutionContext context) { logger.LogInformation($"{context.JobDetail.Key.Name} executed! {DateTime.Now}"); return Task.CompletedTask; } }
已尝试方案
- 自定义JobFactory并注册:
public class SimpleJobFactory(IServiceProvider serviceProvider) : IJobFactory { public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { return (IJob)serviceProvider.GetRequiredService(bundle.JobDetail.JobType); } public void ReturnJob(IJob job) { } } // 在Program.cs的AddQuartz中使用: configurator.UseJobFactory<SimpleJobFactory>();
- 将任务、JobFactory及IScheduler注册为单例:
builder.Services.AddSingleton<SimpleJob>().AddSingleton<SimpleJobFactory>(); builder.Services.AddSingleton(provider => provider.GetRequiredService<ISchedulerFactory>().GetScheduler().GetAwaiter().GetResult());
排查建议
检查Npgsql连接泄漏
- 开启Npgsql连接日志,确认Quartz的ADO JobStore是否正确管理数据库连接,查看是否存在未释放的连接。
- 排查项目中其他涉及Npgsql的操作,确认是否手动创建连接后未释放。
优化Job实例生命周期管理
- 若Job依赖的服务存在生命周期冲突(如单例服务持有短生命周期服务引用),会导致对象无法被GC回收。
- 若Job实现了
IDisposable接口,需在自定义JobFactory的ReturnJob方法中调用Dispose释放资源。
序列化配置调整
- 尝试切换到System.Text.Json序列化(Quartz支持),对比内存占用变化,排查NewtonsoftJson是否存在内存泄漏问题。
- 确认
quartz.jobStore.useProperties设置的合理性,若JobDataMap中存储大量复杂对象,需简化数据结构。
深入内存分析
- 使用Rider内存快照功能,捕获内存占用高的对象,跟踪其引用链,定位被Quartz或DI容器持有的未释放对象。
- 检查静态变量是否持有Job实例或服务引用,导致GC无法回收。
Quartz配置优化
- 调整Quartz线程池大小(
quartz.threadPool.threadCount),避免过多线程持有资源未释放。 - 降低Quartz日志级别,避免大量日志输出导致内存占用增长。
- 调整Quartz线程池大小(
内容的提问来源于stack exchange,提问作者Mutoyoru
相关产品推荐
相关产品推荐

