Windows 服务增加 CPU 消耗
Posted
技术标签:
【中文标题】Windows 服务增加 CPU 消耗【英文标题】:Windows Service Increasing CPU Consumption 【发布时间】:2010-09-06 18:29:23 【问题描述】:在我的工作中,我负责使用 C# 2003 编写的六个 Windows 服务。这些服务中的每一个都包含一个计时器,它每分钟左右触发一次,它们的大部分工作都发生在这里。
我的问题是,随着这些服务的运行,它们开始通过循环的每次迭代消耗越来越多的 CPU 时间,即使它们没有有意义的工作要做(即,它们只是在闲置,看着通过数据库做某事)。当它们启动时,每个服务平均使用(大约)4 个 CPU 的 2-3%,这很好。 24 小时后,每个服务将在其循环运行期间消耗整个处理器。
有人可以帮忙吗?我不知道是什么原因造成的。我们当前的解决方案是每天重新启动一次服务(它们会自行关闭,然后脚本会看到它们处于脱机状态并在凌晨 3 点左右重新启动它们)。但这不是一个长期的解决方案;我担心的是,随着服务变得越来越忙,每天重新启动一次可能还不够……但是由于启动惩罚很大(它们都使用 NHibernate 进行数据访问),随着它们变得越来越忙,正是我们 不想做的是更频繁地重新启动它们。
@akmad:的确,这很困难。
-
是的,独立运行的服务会随着时间的推移显示相同的症状。
不,它没有。我们已经看过了。这可能发生在上午 10 点或下午 6 点或半夜。没有一致性。
我们愿意;他们是。这些服务正在做他们应该做的事,没有别的。
不幸的是,这需要预先知道服务何时会用尽 CPU,这种情况发生在不可预知的时间表上,而且永远不会很快……这让事情变得更加困难,因为我的老板会在何时运行并重新启动它们他们开始遇到问题而不考虑调试问题。
不,他们使用相当一致的 RAM 量(每个大约 60-80MB,机器上的 4GB)。
建议不错,但请放心,我们已经尝试了所有常见的故障排除方法。我希望这是一个有人可能知道的 .NET 问题,我们可以努力解决。我老板的解决方案(我强调不想实现)是在数据库中放置一个字段,该字段包含多次,以便在白天重新启动服务,这样他就可以让问题消失而不去想它.我正在拼命寻找真正问题的原因以便我能够解决它,因为这个解决方案将在大约六个月后变成一场灾难。
@Yaakov Ellis:它们各有不同的功能。一个从异地某处的 Oracle 数据库中读取记录;另一个处理这些记录并将属于这些记录的文件传输到我们的系统;第三个检查这些文件以确保它们是我们所期望的;另一个是维护服务,它不断检查磁盘空间(我们有足够的空间)并轮询其他服务器以确保它们处于活动状态;一个运行只是为了确保所有这些其他的都在运行并完成它们的工作,监控和报告错误,并重新启动任何未能保持整个系统一天 24 小时运行的系统。
所以,如果你问的是我认为你在问什么,不,所有这些服务都没有一件共同的事情(除了通过 NHibernate 访问数据库),我可以指出这是一个潜在的问题。不幸的是,如果事实证明这是真正的问题(这不会让我大吃一惊),整个事情可能会被搞砸——我最终会用简单的 SQL 重写所有这些。我希望这是一个垃圾收集器问题或比 NHibernate 更容易处理的问题。
@Joshdan:没有秘密。正如我所说,我们已经尝试了所有常见的故障排除方法。分析没有帮助:当 CPU 使用率很高时,我们使用的分析器无法指向任何实际执行的代码。大约一个月前,这些服务在寻找这个问题时被拆散了。分析每一段代码,试图找出我们的代码是否是问题所在;我不在这里问,因为我还没有完成我的作业。如果这是一个简单的案例,即服务所做的工作比预期的要多,那就会被抓住。
这里的问题是,在大多数情况下,服务根本不做任何事情,但仍设法消耗 25% 或更多的四个 CPU 内核:它们没有工作可做,并退出循环并等待下一次迭代。从字面上看,这应该几乎不需要 CPU 时间。
这是我们看到的行为示例,在两天内没有工作可做的服务上(在不变的环境中)。这是上周拍摄的:
第 1 天,早上 8 点:平均。 CPU 使用率约 3% 第 1 天,下午 6 点:平均CPU 使用率约 8% 第 2 天,早上 7 点:平均。 CPU 使用率约 20% 第 2 天,上午 11 点:平均。 CPU 使用率约 30%
查看了所有可能的世俗原因后,我在这里提出了这个问题,因为我认为(事实证明是正确的)我会得到更多创新的答案(如 Ubiguchi 的),或指向事物的指针我没有想过(就像 Ian 的建议)。
CPU 峰值也会发生吗 紧接在计时器之前 回调,在定时器回调中, 或紧跟定时器 回调?
你误会了。这不是一个尖峰。如果是,就没有问题;我可以处理尖峰。但它不是...... CPU使用率普遍上升。即使服务什么也不做,等待下一个计时器命中。当服务启动时,一切都很平静,图表看起来像您所期望的那样......通常,使用率为 0%,当 NHibernate 访问数据库或服务执行一些微不足道的工作时,使用率会飙升至 10% .但这会在进程运行时始终增加 25% 的使用率(如果我让它走得太远的话会更多)。
这使得 Ian 的建议成为合乎逻辑的灵丹妙药(NHibernate 会在您不注意的情况下做很多事情)。唉,我已经实施了他的解决方案,但它没有产生效果(我没有证据证明这一点,但我实际上认为这让事情变得更糟......平均使用率似乎上升现在快得多)。请注意,删除 NHibernate“部分”(如您所建议的那样)是不可行的,因为这将删除服务中大约 90% 的代码,这将让我排除计时器问题(我绝对打算尝试),但不能帮助我排除 NHibernate 的问题,因为如果 NHibernate 导致了这种情况,那么实施的狡猾修复(见下文)将不得不成为系统工作的方式;我们在这个项目中非常依赖 NHibernate,以至于 PM 根本不会接受它会导致无法解决的结构问题。
我刚刚注意到一种绝望的感觉 问题——你的问题 除非出现小奇迹,否则会继续下去
不要让它以这种方式脱落。目前,这些服务每天都在重新启动(可以选择输入一天中的任意小时数让它们关闭和重新启动),这可以解决问题,但一旦进入生产机器就不能成为长期解决方案并开始变得忙碌。无论是我修复它们还是 PM 对它们保持这种约束,这些问题都不会继续存在。显然,我更愿意实施真正的修复,但由于最初的测试没有发现原因,而且服务已经经过广泛审查,PM 宁愿让它们重新启动多次,也不愿花更多时间尝试修复它们.这完全超出了我的控制范围,这让你所说的奇迹变得比其他情况更重要。
这非常有趣(到目前为止 因为你信任你的分析器)。
我没有。但是,这些是用 .NET 1.1 编写的 Windows 服务,在 Windows 2000 机器上运行,由一个狡猾的 Nant 脚本部署,使用旧版本的 NHibernate 进行数据库访问。那台机器上几乎没有我会说我信任的东西。
【问题讨论】:
您是否有任何代码可以检测该服务在上一个工作周期中是否仍在“工作”? 您的服务计时器是否有可能过度活跃?比如说,定时器被激活,循环还没有执行,2 分钟过去了。计时器会再次被激活吗?很多事情都可能出错,但所有这些都与您的代码相关。这不是服务的正常行为。 【参考方案1】:您提到您正在使用 NHibernate - 您是否在适当的时间(例如每次迭代结束?)关闭您的 NHibernate 会话?
如果不是,那么加载到内存中的对象映射的大小会随着时间的推移而逐渐增加,并且每次会话刷新会占用越来越多的 CPU 时间。
【讨论】:
我不相信我们是。这在某种程度上是我们与 NHibernate 相关的开发中的一种模式。会议持续了很长时间。这是一个有趣的可能性,谢谢。【参考方案2】:这是我要开始的地方:
-
获取 Process Explorer 并显示 JIT 中的 %Time、GC 中的 %Time、CPU 周期增量、CPU 时间、CPU % 和线程。
您还需要内核和用户时间,以及一些有代表性的堆栈跟踪,但我认为您必须点击属性才能获取快照。
比较拍摄前后。
关于可能性的一些想法:
excessive GC(% Time in GC 增加。此外,Perfmon GC 和 CPU 计数器会对应) 过多的线程和相关的上下文切换(线程数上升) 轮询(堆栈跟踪始终在单个函数中捕获) 内核时间过长(内核时间很高 - 任务管理器在 CPU 较高时显示较大的内核时间数) 异常(PE .NET 选项卡抛出的异常很高并且越来越高。还有一个 Perfmon 计数器) virus/rootkit(好的,这是最后的方案 - 但可以构建一个隐藏在 TaskManager 中的 rootkit。我怀疑如果你足够狡猾,你可以将不可避免的 CPU 使用分配给另一个进程. 此外,如果您已经排除了上述所有情况,我现在没有想法)【讨论】:
【参考方案3】:远程调试你的未知应用程序显然非常困难......但这里有一些事情我会看看:
-
当您一次只运行一项服务时会发生什么?你还看到减速吗?这可能表明服务之间存在争用。
无论服务运行了多长时间,问题是否总是在同一时间出现?这可能表明其他原因(备份、病毒扫描等)导致计算机(或数据库)整体变慢。
您是否有日志记录或其他机制来确保服务只按照您认为的频率工作?
如果您可以在短时间内看到性能下降,请尝试运行该服务一段时间,然后附加一个分析器以查看究竟是什么在与 CPU 挂钩。
你没有提到任何关于内存使用的事情。你有这些服务信息吗?您可能会用尽大部分 RAM 并导致磁盘成为垃圾,或出现类似问题。
祝你好运!
【讨论】:
【参考方案4】:我建议将问题分解成碎片。 首先,找到一种100% 的快速重现问题的方法。降低计时器,以使服务更频繁地启动(例如,比正常速度快 10 倍)。如果问题出现的速度快了 10 倍,那么它与迭代次数有关,而不是与实时或服务完成的实际工作有关)。您将能够比每天一次更快地完成接下来的步骤。 其次,注释掉所有真正的工作代码,只留下服务、定时器和同步机制。如果问题仍然出现,那么它将出现在代码的那部分。 如果没有,然后开始添加您注释掉的代码,一次一个。最终,您应该找出导致问题的代码部分。
【讨论】:
【参考方案5】:'恐怕这个答案只会为您提供一些参考方向,但在 .NET Windows 服务中看到类似问题后,我有一些想法可能会对您有所帮助。
我的第一个建议是,您的服务在处理内存的方式或处理非托管内存的方式上可能存在一些错误。上次我追查到一个类似的问题时,结果发现我们正在使用存储句柄来存储静态内存中的非托管对象的第三方 OSS 库。服务运行的时间越长,服务获取的句柄就越多,这导致进程的 CPU 性能迅速下降。尝试解决此类问题以确保您的服务在计时器调用之间不存储任何内容的方法,尽管如果您的 3rd 方库使用静态内存,您可能必须做一些聪明的事情,例如为计时器调用创建一个应用程序域并放弃处理完成后的应用程序域(及其静态内存)。
我在类似情况下看到的另一个问题是定时器同步代码存在问题,这实际上允许多个线程同时运行处理代码。当我们调试代码时,我们发现第一个线程阻塞了第二个线程,当第二个线程启动时,第三个线程被阻塞了。随着时间的推移,阻塞持续的时间越来越长,因此 CPU 使用率达到顶峰。我们用来解决这个问题的解决方案是实现正确的同步代码,这样计时器只会在它不会被阻塞的情况下启动另一个线程。
希望这会有所帮助,但如果我的两个想法都是红鲱鱼,请提前道歉。
【讨论】:
线程不是问题;这是我大约 18 个月前处理的事情,我相信在任何给定时间只有一个计时器调用正在运行。不过好主意,谢谢。我将研究 AppDomain 位。虽然我不知道任何内存问题,但如果我可以在每次计时器命中后放弃整个执行上下文,那可能会解决我们遇到的任何问题。感谢您的建议。【参考方案6】:听起来像是计时器的线程问题。您可能有一个工作单元阻塞了另一个在不同工作线程上运行的工作,导致它们在每次计时器触发时堆积起来。或者,您的实例的生活和工作时间可能比您预期的要长。
我建议重构计时器。将其替换为在 ThreadPool 上排队工作的单个线程。您可以 Sleep() 线程来控制它寻找新工作的频率。确保这是您的代码是多线程的唯一地方。当工作准备好进行处理并在工作完成后销毁时,应实例化所有其他对象。状态是多线程代码中的敌人。
设计缺乏的另一个方面似乎是您有多个服务轮询资源来做某事。我建议将它们统一在一个服务下。他们可能会做不同的事情,但他们会齐心协力;您只是使用文件系统、数据库等作为方法调用的替代品。还有,2003?我为你感到难过。
【讨论】:
谢谢。不幸的是,我无法控制有多少服务正在运行。我完全同意其中大部分内容应该合并。不幸的是,PM 确信运行六项服务来完成这项工作更有效。 :S 大约两年来,我也一直在谈论升级到新的 .NET 版本。那没有任何影响。现在我们有更多的代码(大部分是 ASP.NET),这将使升级比我第一次建议时更加困难。我相当有信心,工作没有堆积起来。如果一个服务在它的定时器触发时已经在工作,那么【参考方案7】:建议不错,但请放心,我们已经尝试了所有常见的故障排除方法。我希望这是一个有人可能知道的 .NET 问题,我们可以努力解决。
我的感觉是,无论根本原因多么奇怪,通常的故障排除步骤都是找出问题的最佳选择。
由于这是一个性能问题,因此良好的测量结果非常宝贵。整个进程 CPU 使用率的测量范围太广了。 您的服务在哪里花费时间?您可以使用分析器来测量这一点,或者只记录各个部分的开始和停止。如果您甚至无法做到这一点,请使用 Andrea Bertani 的建议 - 通过删除其他部分来隔离部分。
一旦确定了大致区域,就可以进行更细粒度的测量,直到找出 CPU 使用率的来源。如果在这一点上如何解决它并不明显,那么您至少有足够的弹药来解决一个更具体的问题。
如果您实际上已经完成了所有这些常见的故障排除,请告诉我们秘密。
【讨论】:
以上是关于Windows 服务增加 CPU 消耗的主要内容,如果未能解决你的问题,请参考以下文章
如何从 Windows 驱动程序(又名内核空间)确定 CPU 和内存消耗
为啥在安装Visual Studio 2005之后,开机时"SVCHOST.EXE"进程占用CPU很高?且内存消耗将近100MB!?