我有一个C#服务,它侦听XML消息的队列,接收它们,使用XSLT处理它们并将它们写入数据库。 它每天处理大约60K个消息,每个消息大约1Mb。空闲时的内存下降到100MB,这真的很好。 但是最近我开始处理大小为12 MB的消息。它确实会耗尽内存,即使在空闲时它也有大约500MB的内存。任何建议为什么这可能是一个问题?我认为没有内存泄漏,因为它会在处理了这么多(60K消息1MB)之后浮出水面。
答案 0 :(得分:7)
看起来非常好。为什么你认为这是一个问题?
垃圾收集器最终将释放未使用的内存,但这并不意味着只要您的服务空闲就会发生这种情况。
Raymond Chen撰写了一篇很好的文章,解释了垃圾收集的基本思想:
<强> Everybody thinks about garbage collection the wrong way 强>
然而 - 但这是对您的问题中给出的信息的纯粹推测 - 可能存在与XSLT中的扩展方法相关的内存泄漏。如果在每次转换新的XML文档时重新编译样式表,扩展方法可能会导致问题。修复很简单:编译完成后,缓存样式表。
答案 1 :(得分:5)
SIR!放下任务经理并走出去。认真。 .NET中的内存管理并未针对最小的占用空间进行调整。它针对效率进行了调整。它将保留在内存中而不是将其释放回系统。除非存在实际问题(OOM异常,系统问题),否则要抵制小心记忆的诱惑。
答案 2 :(得分:3)
您使用什么衡量记忆?有很多可能的措施,其中没有一个真正意味着“用过的记忆”。使用虚拟内存,共享图像等事情并不简单。
即使在空闲时也有大约500MB的内存
大多数进程(我认为这包括.NET)一旦分配给进程,就不会将内存释放回操作系统。但如果它没有被使用,它将从物理内存中分页,允许其他进程使用它。
开始处理大小为12 MB的邮件
如果将XML文档扩展为对象模型(例如XmlDocument
),则需要更多内存(许多链接到其他节点)。要么看一个不同的模型(XDocument
和XPathDocument
都是较轻的权重),或者更好的是,用XmlReader
处理XML,因此永远不会有完全扩展的对象模型。
答案 3 :(得分:2)
首先,在系统中添加一些内容,以允许您手动调用垃圾收集以进行调查。除非供应短缺,否则CLR不一定会将内存返回给操作系统。当系统处于静止状态时,您应该使用此机制手动调用垃圾收集,以便您可以查看内存是否会降低。 (您应该两次调用GC.Collect(2)
以确保实际收集具有终结器的对象,而不是最终确定。)
如果在完整的GC之后内存下降到静止级别,那么你没有问题:只是.NET没有主动解除内存。
如果内存没有下降,那么你会有某种泄漏。由于您的消息很大,这意味着它们的文本表示可能最终会出现在大对象堆(LOH)上。这个特定的堆没有被压缩,这意味着泄漏这个内存比使用其他堆更容易。
需要注意的一点是字符串实习:如果你是手动实习字符串,这可能导致LOH碎片。
答案 4 :(得分:1)
很难说可能是什么问题。在调查内存问题时,我使用Ants Memory Profiler取得了成功。
答案 5 :(得分:1)
与思想相反我认为没有内存泄漏;我想profile记忆。
答案 6 :(得分:0)
我会检查你的事件处理程序。如果您在完成后不小心分离它们,似乎很容易创建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);
}
}