长时间运行后的高CPU使用率
我的应用程序出现问题,希望有人能给我建议如何解决它。长时间运行后的高CPU使用率
我有多线程应用程序。它调整10-20个线程,并在每个线程中执行一些复杂的任务。
Thread thread = new Thread(ProcessThread);
thread.Start();
private void ProcessThread()
{
while(IsRunning)
{
// do some very complex operations: grab HTTP pages. Save to files. Read from files. Run another threads etc.
}
}
在开始应用使用了约10%的CPU和140MB内存。但1000后执行CPU使用率为25%-30%,内存为1200Mb。我知道我的代码中可能存在内存泄漏,我会尝试修复它。但是,CPU发生了什么?它为什么会增长?每次执行都会执行与开始和稍后相同的操作(例如,打开网页,获取一些信息并将其保存到文件中)。
我认为这个问题可以与GC。更多的内存应用程序需要更多的CPU需要清理内存?
另一个问题,能否请你指教一个很好的工具如何衡量什么占用CPU在我的应用程序?
也许你可以推荐一个很好的工具来分析内存,并检查它泄漏?我试过JetBrains dotMemory,但是不太了解。也许你可以帮助我。 以下是统计: http://prntscr.com/dev067 http://prntscr.com/dev7a2 正如我所看到的,我没有太多非托管内存。但同时我看到字符串的问题,但不能理解GC应该清除它的问题?
欣赏任何意见和建议,有什么我可以提高。
除非你是包装原生类型,我怀疑你有内存泄漏。内存使用情况很可能是由于GC的工作原理。
GC将不会收集在一个周期内都死了的项目。它将使用世代。这意味着只有在需要空间时才会收集旧对象。所以内存使用量会增加,直到第一次收集。然后,只收集一小部分的内容,这会降低内存使用量,但并不完全。 GC也不一定会将释放的内存返回给操作系统。
在Visual Studio中的更高版本,你会发现一个内存分析器。
这是非常不清楚什么是“一些复杂的任务”。 这尤其让我担心:
// do some very complex operations: grab HTTP pages. Save to files. Read from files. Run another threads etc.
好吧,如果你确实是从你的进程,线程开始新的线程,那么它完全是有道理的,CPU使用率提高。从性能角度来看,创建新线程代价高昂,这也是我们使用线程池(回收线程)的原因。
内存使用情况也是如此。更多的线程=为每个线程需要更多内存(每个线程都有它自己的堆栈需要额外的内存)...
另外,我不认为GC是罪魁祸首这里。
有很多问题需要TE回答之前,我可以帮你找到这样的行为的原因问题,我们可以责怪GC之前:):你启动所有 1)做,比方说,20个线程在你的节目开始时? 2)你是否已经从正在运行的创建新线程? 3)这些线程的终止条件是什么?他们真的终止了吗?
我会建议使用dotTrace确定CPU使用率。 不幸的是,我没有使用任何工具来分析内存使用情况,所以我不能推荐任何。
我看着你的截图,我看到你有很多对象,这些对象正在幸存的第0代集合中,所以它们被升级到第1代,然后升级到第2代。这可能是内存泄漏的标志,但不一定。没有看到你的代码很难说。我能够告诉你的是,你正在保留你的物品很长一段时间。这可能是需要的,但再次看不到代码我不知道。
约GC
有点当GC醒来清理,分析了托管堆,看看哪些对象没有扎根,标志着他们都可以进行收集。这被称为标记阶段。然后它开始释放内存。这叫做扫描阶段。如果无法清除任何东西,这些对象可以代1.一段时间后的GC再次唤醒并重复上面的,但是这一次,由于在第1代的项目,如果他们无法收集他们会去代2.本可能是一个不好的迹象。这些物体是否真的需要在那里长时间存在?
那么你能做什么?
你说GC必须清理东西。那么是的,GC会清理的东西,但只有当你不参考它们。如果一个对象被植入,GC将无法清除它。记住
与GC
编写代码开始调查,你需要调查你的代码,并确保你的代码是在考虑GC写的。浏览你的代码并列出你正在使用的所有对象。如果任何对象是实现IDisposable一类的,然后将它们包装在using语句:
using (Font font1 = new Font("Arial", 10.0f))
{
// work here depends on font1
}
如果你在你的类的类级变量的一个需要font1
,那么你就必须做出descision时调用Dispose
就可以了:至少,这个类必须实现IDisposable并调用Dispose font1
。这个类的用户(任何在这个类上调用new的代码)应该使用这个类,并使用using
语句或者调用它的Dispose。
不要让物体的时间比你需要他们。
还需要进一步调查吗?
一旦您调查了您的代码并确保您的代码对GC更友善,并且仍然存在问题,请使用工具进行调查。 Here是一篇很好的文章,可以帮助您理解这部分内容。
有些人有误解认为调用GC.Collect()
将解决内存泄漏问题。这根本不是真的。强制垃圾回收仍然会遵循相同的规则,如果对象是根深蒂固,你可以无限拨打GC.Collect()
,对象仍然不会被清理。
这里是一个示例应用程序,它会显示这样的:
public class Writer : IDisposable
{
public void Dispose()
{
}
public void Write(string s)
{
Console.WriteLine(s);
}
}
class Program
{
static void Main(string[] args)
{
Writer writer = new Writer();
writer.Write("1");
writer.Dispose();
// writer will still be around because I am referencing it (rooted)
writer.Write("2");
GC.Collect();
// calling GC.Collect() has no impact since writer is still rooted
writer.Write("3");
Console.ReadKey();
}
}
我只能对你为什么有一个CPU问题,而不是GC评论。
- 我建议您验证您是否只创建您期望的数字线程。如果你碰巧有线程启动线程,很容易滑落。
- 正如另一条评论所述,线程创建和销毁代价高昂。如果您一直在创建和销毁线程,这将对您的性能产生显着影响。
- 我怀疑你的放缓的原因是内存抖动,这意味着每个线程使用的内存量足够大,和/或所有线程使用的总量足够大,导致内存页被换入和换出每时每刻。在某些情况下,处理器花费更多的时间交换内存到磁盘上,而不是花在执行线程上。
这里是我的建议:
- 使用一个线程池。这可以最大限度地减少创建和销毁线程所需的时间。这也会限制你正在使用的线程数量。
- 确保每个线程在被换出之前执行一段合理的时间 - 即你没有线程上下文抖动。
- 确保每个线程正在使用的内存量并不比预期的大得多,以防止内存抖动。
- 如果您必须为每个线程使用大量的内存,请确保您的使用模式是这样的,以至于没有太多的内存分页。
用"Start collecting allocations data immediately" enabled运行你的应用程序,过一会儿看快照,看看at memory traffic view。 要了解什么占用内存打开"All objects" grouped by type并查看哪些类型占用大量内存。
对于性能分析,我可以推荐JetBrains dotTrace(时间线模式,为你的情况)。