我有一个Windows服务,它会定期检查数据库中的新记录并处理新线程中的每条记录(最多可能需要10分钟)。当服务在它启动后立即空闲时,它需要4 MB的RAM。当它开始处理第一个记录时,它会上升到70+ MB,并且即使线程完成也会保持在那里(我想这很好,因为很快就会再次需要这个内存)。然后在下一个请求它从70到大约100 MB并且在线程完成后也停留在那里。 所以我的问题是,在这样的事情中会发生内存泄漏:
public partial class MyWinService : ServiceBase
{
DBService service;
IEnumerable<long> unfinishedRequests;
List<long> activeRequests;
protected override void OnStart(string[] args)
{
System.Timers.Timer timer1 = new System.Timers.Timer(60000);
timer1.Elapsed += Timer_Tick;
timer1.Start();
service = new DBService();
activeRequests = new List<long>();
}
private void Timer_Tick(object sender, System.Timers.ElapsedEventArgs e)
{
unfinishedRequests = service.GetUnfinishedRequests();
foreach (var req in unfinishedRequests)
{
new Thread(delegate() { ProcessRequest(req); }).Start();
}
}
private void ProcessRequest(long requestID)
{
activeRequests.Add(requestID);
// Lots of webservice calls, XML->XSLT->HTML->PDF, byte arrays, database INSERTs & UPDATEs etc.
activeRequests.Remove(requestID);
}
}
在线程完成后,不应该销毁在ProcessRequest()方法中创建的所有对象吗?如果程序在第一个线程之后已经有100 MB内存,为什么它要求更多(在我的测试中输入数据是相同的,所以两个线程应该使用相同数量的内存,我认为)。
答案 0 :(得分:2)
原来我太依赖静态助手类了。一旦我将静态更改为实例,内存消耗明显减少,GC也能够释放更多内存
答案 1 :(得分:1)
在不了解ProcessRequest功能的更多细节的情况下回答您的问题并不容易。
您可以尝试使用任何可用的内存分析器
自行查找内存泄漏问题答案 2 :(得分:1)
至少有两件事可能会出现内存泄漏。
首先,您的activeRequests
列表会不断添加,但从未调整过大小。虽然您从列表中删除项目,但永远不会回收分配用于保存这些项目的内存。如果您向列表中添加了一百万个项目,然后将它们全部删除,那么存储这些项目的基础内存仍将被分配。
尽管如此,你的列表必须非常大(约400万件)才能弥补30兆字节。
你说你的计时器滴答方法可能需要10分钟才能完成。你有一个问题,因为虽然计时器滴答很快就会完成,但它创建的线程可能需要很长时间才能执行。一分钟后,另一个计时器滴答声将出现并排队更多线程。你对这里的线程没有限制,所以如果有一千个未完成的请求,你将完成1,000个线程。这绝对不是一件好事,而且很可能以这种方式耗尽内存。
您应该考虑在该循环中使用Parallel.ForEach
。例如:
Parallel.ForEach(unfinishedRequests, req => ProcessRequest(req));
这将限制并发线程的数量,这将阻止您有太多线程导致内存耗尽或导致系统由于过多的任务切换而变慢。
我不知道你所谓的内存泄漏是否真的是内存泄漏。如果您使用任务管理器来确定进程正在使用的内存量,那么您不会测量实际内存使用量。除非你真的泄漏严重,否则任务管理器对查找内存泄漏毫无用处。搜索SO以获取有关C#内存泄漏检测的信息。