2016-12-03 33 views
0

我的应用程序出现问题,希望有人能给我建议如何解决它。长时间运行后的高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应该清除它的问题?

欣赏任何意见和建议,有什么我可以提高。

回答

0

除非你是包装原生类型,我怀疑你有内存泄漏。内存使用情况很可能是由于GC的工作原理。

GC将不会收集在一个周期内都死了的项目。它将使用世代。这意味着只有在需要空间时才会收集旧对象。所以内存使用量会增加,直到第一次收集。然后,只收集一小部分的内容,这会降低内存使用量,但并不完全。 GC也不一定会将释放的内存返回给操作系统。

在Visual Studio中的更高版本,你会发现一个内存分析器。

0

这是非常不清楚什么是“一些复杂的任务”。 这尤其让我担心:

// 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

我看着你的截图,我看到你有很多对象,这些对象正在幸存的第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(); 

    } 
} 
0

我只能对你为什么有一个CPU问题,而不是GC评论。

  1. 我建议您验证您是否只创建您期望的数字线程。如果你碰巧有线程启动线程,很容易滑落。
  2. 正如另一条评论所述,线程创建和销毁代价高昂。如果您一直在创建和销毁线程,这将对您的性能产​​生显着影响。
  3. 我怀疑你的放缓的原因是内存抖动,这意味着每个线程使用的内存量足够大,和/或所有线程使用的总量足够大,导致内存页被换入和换出每时每刻。在某些情况下,处理器花费更多的时间交换内存到磁盘上,而不是花在执行线程上。

这里是我的建议:

  1. 使用一个线程池。这可以最大限度地减少创建和销毁线程所需的时间。这也会限制你正在使用的线程数量。
  2. 确保每个线程在被换出之前执行一段合理的时间 - 即你没有线程上下文抖动。
  3. 确保每个线程正在使用的内存量并不比预期的大得多,以防止内存抖动。
  4. 如果您必须为每个线程使用大量的内存,请确保您的使用模式是这样的,以至于没有太多的内存分页。