C# NET中的内存问题
我有一个C#服务,它监听XML消息队列,接收它们,使用XSLT处理它们,并将它们写入数据库。 它每天处理大约60K条消息,每条消息大约1Mb。空闲时的内存将下降到100MB,这非常好。C# NET中的内存问题,c#,.net,C#,.net,我有一个C#服务,它监听XML消息队列,接收它们,使用XSLT处理它们,并将它们写入数据库。 它每天处理大约60K条消息,每条消息大约1Mb。空闲时的内存将下降到100MB,这非常好。 但是最近我开始处理12MB大小的邮件。它确实会耗尽内存,即使在空闲时,它也有大约500MB的内存。有什么建议可以解释为什么这会成为一个问题吗?我不认为存在内存泄漏,因为在处理了这么多(60K条1MB的消息)后,它会出现泄漏。看起来很好。你为什么认为这是个问题 垃圾收集器最终会释放未使用的内存,但这并不意味着一旦您
但是最近我开始处理12MB大小的邮件。它确实会耗尽内存,即使在空闲时,它也有大约500MB的内存。有什么建议可以解释为什么这会成为一个问题吗?我不认为存在内存泄漏,因为在处理了这么多(60K条1MB的消息)后,它会出现泄漏。看起来很好。你为什么认为这是个问题 垃圾收集器最终会释放未使用的内存,但这并不意味着一旦您的服务空闲,就会发生这种情况 Raymond Chen写了一篇很好的文章,解释了垃圾收集的基本思想:
然而——但这纯粹是猜测问题中给出的信息——XSLT中可能存在与扩展方法相关的内存泄漏。如果每次转换新的XML文档时都重新编译样式表,扩展方法可能会导致问题。修复方法很简单:编译后,缓存样式表。很难说问题出在哪里。我在调查内存问题时成功地使用了。与我认为没有内存泄漏的想法相反;我想要记忆。你用什么来衡量记忆?有很多可能的衡量标准,但没有一个真正意味着“使用过的记忆”。有了虚拟内存,图像共享等事情并不简单 即使在空闲时,它也有大约500MB的内存 大多数进程(我认为这包括.NET)在分配给进程后不会将内存释放回操作系统。但如果它不在使用中,它将从物理内存中分页,从而允许其他进程使用它 已开始处理大小为12 MB的邮件
如果将XML文档扩展为对象模型(例如),它需要更多的内存(指向其他节点的大量链接)。要么看一个不同的模型(两者都较轻),要么更好地用XML处理,这样就永远不会有一个完全扩展的对象模型。先生!放下任务经理,走开。认真地NET中的内存管理未针对最小占用空间进行调整。它是为提高效率而调整的。它将保留内存,而不是将其释放回系统。除非出现实际问题(OOM异常、系统问题),否则请抵制保护内存的诱惑。首先,在系统中添加一些内容,以允许您手动调用垃圾收集进行调查。除非内存不足,否则CLR不一定会将内存返回到O/S。当系统处于静态状态时,应该使用此机制手动调用垃圾收集,以便查看内存是否下降。(您应该调用两次
GC.Collect(2)
,以确保带有终结器的对象实际被收集,而不仅仅是被终结。)
如果在完全GC之后内存下降到静态级别,那么您就没有问题了:只是.NET在释放内存时没有主动性
如果内存没有下降,那么就有某种泄漏。由于您的消息很大,这意味着它们的文本表示可能最终会出现在大型对象堆(LOH)上。这个特定的堆没有被压缩,这意味着它比其他堆更容易泄漏内存
需要注意的一件事是字符串插入:如果手动插入字符串,这可能会导致LOH碎片。我会检查您的事件处理程序。如果在完成这些操作时不小心将其分离,那么创建GC不会收集的对象引用似乎很容易。
我不知道这是最好的实践(因为开始和取消事件处理的责任应该落在订阅者身上),但我已经实现了IDisposable,并使用显式委托字段实例创建了我的事件。处置时,该字段可以设置为null,这将有效地分离所有订阅 大概是这样的:
public class Publisher
: IDisposable
{
private EventHandler _somethingHappened;
public event EventHandler SomethingHappened
{
add { _somethingHappened += value; }
remove { _somethingHappened -= value; }
}
protected void OnSomethingHappened(object sender, EventArgs e)
{
if (_somethingHappened != null)
_somethingHappened(sender, e);
}
public void Dispose()
{
_somethingHappened = null;
}
}
除此之外(我不知道您对发布服务器有多大的控制权),您可能需要小心分离订阅服务器上不需要的处理程序:
public class Subscriber
: IDisposable
{
private Publisher _publisher;
public Publisher Publisher
{
get { return _publisher; }
set {
// Detach from the old reference
DetachEvents(_publisher);
_publisher = value;
// Attach to the new
AttachEvents(_publisher);
}
}
private void DetachEvents(Publisher publisher)
{
if (publisher != null)
{
publisher.SomethingHappened -= new EventHandler(publisher_SomethingHappened);
}
}
private void AttachEvents(Publisher publisher)
{
if (publisher != null)
{
publisher.SomethingHappened += new EventHandler(publisher_SomethingHappened);
}
}
void publisher_SomethingHappened(object sender, EventArgs e)
{
// DO STUFF
}
public void Dispose()
{
// Detach from reference
DetachEvents(Publisher);
}
}
我希望当服务空闲且发生垃圾回收时,内存会完全耗尽。此外,如果服务中出现许多这样的巨大消息,它会抛出内存不足异常。@koumides:当服务空闲时,您的内存不会立即释放,垃圾收集器会根据分配情况进行优化,以便在最佳时间释放内存。您如何衡量内存使用情况?任务管理器不是最好的方法。。。像CLR事件探查器这样的事件探查器可以让您更好地了解分配和使用了多少内存。@koumides re xltt转换的可能重复不是问题/假设它不会在内存中编译/加载模板一段时间。。。这只是一个猜测,但你仍然不能放弃这样的理由;)ps使用探查器。编译器对字符串文本的插入是否会导致相同的碎片?@Matt Briggs:不,编译器实际上并不执行任何插入操作,而是以这样的方式编译所有字符串文本:当程序启动时,只会创建一个字符串对象。