我的应用程序存在问题,希望有人可以给我建议如何修复它。
我有多线程应用程序。它可以调整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.
}
}
一开始app使用大约10%的CPU和140Mb的内存。但在1000次执行后,CPU使用率为25%-30%,内存为1200Mb。我知道可能我的代码中有内存泄漏,我会尝试修复它。但是CPU发生了什么?它为何成长?每次执行都会执行与开始和稍后相同的操作(例如,打开网页,获取一些信息并将其保存到文件中)。
我认为这个问题可能与GC有关。更多的内存应用需要更多的CPU需要清理内存吗?
另一个问题,请问一个好的工具如何衡量我的应用程序中CPU的含量?
也许你可以推荐一个好的工具来分析内存并检查泄漏的位置?我试过JetBrains dotMemory,但并不太了解。也许你可以帮助我。 这是统计: http://prntscr.com/dev067 http://prntscr.com/dev7a2 正如我所见,我没有太多非托管内存。但与此同时,我发现字符串存在问题,但无法理解GC必须清除它的错误吗?
感谢我可以改进的任何建议和建议。
答案 0 :(得分:0)
除非你包装本机类型,否则我怀疑你有内存泄漏。内存使用率很可能是由于GC的工作方式。
GC不会收集循环中的所有死物品。它将使用代。这意味着只有在需要空间时才会收集旧对象。因此,内存使用量将增加,直到第一次收集。然后,只收集了一小部分项目,这会稍微降低内存使用量,但不会完全降低。 GC也不一定会将释放的内存返回给操作系统。
在Visual Studio的高版本中,您将找到一个内存分析器。
答案 1 :(得分:0)
目前尚不清楚什么是“一些复杂的任务”。 这特别让我担心:
// do some very complex operations: grab HTTP pages. Save to files. Read from files. Run another threads etc.
好吧,如果你确实从你的进程线程开始新线程,那么提高CPU使用率是完全有意义的。从性能角度来看,创建新线程的成本很高,这就是我们使用线程池(回收线程)的原因。
内存使用也是如此。更多线程=每个线程需要更多内存(每个线程都有自己的堆栈,需要额外的内存)...
另外,我不相信GC是罪魁祸首。
在我能帮助你找到这种行为的原因之前,有很多问题需要回答,在我们责备GC之前:): 1)你是否在你的程序开始时开始所有20个线程? 2)你是否从已经运行的线程创建新线程? 3)这些线程的终止条件是什么?他们真的会终止吗?
我建议使用dotTrace来确定CPU使用率。 不幸的是,我没有使用任何工具来分析内存使用情况,所以我不推荐任何工具。
答案 2 :(得分: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
作为类级变量,那么您必须在其上调用Dispose
时做出决定:至少这个类应该实现IDisposable和呼叫font1
上的处理。此类的用户(在此类上调用new的任何代码)应该将此类与using
语句一起使用,或者调用`Dispose'在它上面。
不要让物品长于你需要的时间。
还需要更多调查吗?
一旦您调查了代码并确保您的代码对GC更友好并且您仍然遇到问题,那么请使用工具进行调查。 Here是一篇很好的文章,可以帮助您完成这一部分。
有些人误以为调用GC.Collect()
会解决内存泄漏问题。这根本不是真的。强制垃圾收集仍然遵循相同的规则,如果对象是root用户,则可以无限期地调用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();
}
}
答案 3 :(得分:0)
我只能评论您遇到CPU问题而不是GC问题。
以下是我的建议:
答案 4 :(得分:0)
使用"Start collecting allocations data immediately" enabled运行您的应用程序,稍后获取快照并查看at memory traffic view。 要了解占用内存的内容"All objects" grouped by type并查看哪些类型占用的内存最多。
对于性能分析,我可以推荐JetBrains dotTrace(时间轴模式,适用于您的情况)。