Task.Factory未释放内存?Windows服务内存持续增长问题
你的代码中Task.Factory.StartNew的使用确实存在潜在问题,同时内存持续增长可能由多个因素共同导致,以下是具体分析和修复建议:
一、Task.Factory.StartNew的核心问题
闭包引用陷阱(针对C# 4.0及更早版本)
旧版C#的foreach循环中,循环变量session是共享的,所有通过Task.Factory.StartNew创建的任务会持有同一个session的引用,导致这些session对象及关联资源无法被垃圾回收器及时回收。即使是C# 5.0+(循环变量每次迭代独立),也建议显式捕获变量避免意外:foreach (var session in sessions) { var currentSession = session; // 显式捕获当前迭代的session Task.Factory.StartNew(() => MyProcessTask(currentSession)); }未指定线程池调度器
Task.Factory.StartNew默认使用当前上下文的TaskScheduler,在Windows服务的Timer事件回调中,可能导致任务绑定到非线程池调度器,额外占用线程资源。建议显式指定使用线程池调度器:Task.Factory.StartNew(() => MyProcessTask(currentSession), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);未处理任务内部异常
外层的try/catch无法捕获Task内部抛出的异常,未被观察到的异常会保留在Task对象中,导致Task无法被GC回收,最终造成内存泄漏。必须处理任务内部的异常:Task.Factory.StartNew(() => MyProcessTask(currentSession)) .ContinueWith(task => { if (task.Exception != null) { // 此处添加异常日志记录,不要空吞异常 // LogError(task.Exception); } }, TaskContinuationOptions.OnlyOnFaulted);
二、Timer使用的并发问题
你在Elapsed事件中先Stop再Start,但如果任务执行总耗时超过10秒,下一轮Timer事件会再次触发并启动新的任务,导致任务堆积、线程池线程持续增加,内存占用飙升。建议添加并发控制:
private int _processingFlag = 0; // 用Interlocked实现线程安全的标记 private void ElapsedEvent(object sender, ElapsedEventArgs e) { // 检查是否已有任务在执行,避免并发 if (Interlocked.CompareExchange(ref _processingFlag, 1, 0) == 1) return; try { var sessions = GetActiveSessions(); foreach (var session in sessions) { var currentSession = session; Task.Factory.StartNew(() => { try { MyProcessTask(currentSession); } catch (Exception ex) { // 记录任务内部异常 } }, TaskCreationOptions.None, TaskScheduler.Default); } } catch (Exception ex) { // 记录GetActiveSessions等环节的异常 } finally { Interlocked.Exchange(ref _processingFlag, 0); t.Start(); } }
三、内存泄漏的其他排查方向
- MyProcessTask资源未释放:检查
MyProcessTask中是否使用了非托管资源(如数据库连接、文件句柄、网络套接字),确保这些资源在使用后被正确关闭或释放。 - Session对象的长生命周期引用:检查
GetActiveSessions返回的session对象是否持有静态变量、事件订阅等长生命周期引用,导致对象无法被GC回收。 - Timer类型选择:确认你使用的是
System.Timers.Timer(适合服务端),而非System.Windows.Forms.Timer(仅适用于WinForm),后者可能存在上下文绑定问题导致资源泄漏。
总结
Task.Factory.StartNew的不当使用(闭包陷阱、异常未处理、调度器未指定)是内存增长的潜在原因,同时Timer的并发控制缺失、任务内部资源未释放也可能加剧问题。按照上述方案调整代码后,可有效缓解内存泄漏问题。
内容的提问来源于stack exchange,提问作者markzzz

