.NET 4中WCF的内存问题

5
我遇到了一个问题,我的ASP.NET 4 MVC 2 + WCF应用程序在Windows 2008 64位应用程序中进行负载测试时会使用大量内存,几分钟后就会使用掉几乎所有可用内存(8 GB)(我们有几个工作进程在运行)。
使用ANTS Memory Profiler进行分析后,它显示了一些有趣的结果:
1. .NET托管内存从15 MB增加到40 MB,但这归因于我们在程序中使用的缓存机制。然而,.NET本身分配了近180 MB的空闲空间,这是意外的。
2. 非托管内存大小显著增加,直到负载测试运行约3分钟后达到120 MB(尽管我们的应用程序没有明确使用任何P/Invoke或COM对象。但是我们确实使用了一些COM+对象,在finally块中使用后被释放)。
3. 内存变得分散。
以上两点导致整个应用程序在负载测试运行几分钟后使用约350 MB,但如果我们不停止测试,它将继续增长。
基于以上第一点,我测试了一些应用程序,以测试问题是否由我们的应用程序或WCF引起。当逻辑存储在EXE程序中时,应用程序仅使用24 MB私有字节中的200 KB(开始时额外使用了120 KB未使用的内存),并在完成后释放(这是可以接受的);但当逻辑存储在WCF中时,应用程序使用了66 MB托管内存(从开始的61 MB增加到64 MB的空闲/未使用托管内存)。因此,似乎是WCF/ASP.NET导致内存大量增加。
以下是需要回答的问题:
1. 为什么.NET在堆中分配了这么多的空闲空间?虽然理解空闲空间可能是在内存快照过程中进行GC的某些Gen 0/Gen 1/Gen 2,但我不认为应用程序真正使用了那么多内存。
2. WCF的行为是否正常?如果是,有没有办法改变它的行为,使其使用更少的内存?
3. 如何找到非托管内存泄漏,特别是我没有明确使用非托管代码的情况下?

1
这里没有什么有用的信息...使用350 MB可能是正常的,离8GB还有很长的路要走。稍后运行您的分析器,然后使用它来找出内存去了哪里。 - H H
WCF 的调用频率有多高?你可能正在看到用于处理尚未收集的消息的临时缓冲区。 - Panagiotis Kanavos
3个回答

5

WCF使用临时缓冲区来处理消息。你认为的内存泄漏可能是尚未收集的临时缓冲区。

为了避免一直创建新的缓冲区,WCF使用BufferManager重复使用缓冲区,最多可以达到maxBufferPoolSize(链接到此处here)所指定的限制,默认为512KB。超过此限制的任何请求都会导致创建新的不可重用的缓冲区,必须进行垃圾回收。

检查的另一个选项是maxBufferSize,它限制可以由BufferManager返回的最大缓冲区大小。较大的缓冲区不会被池化,必须进行垃圾回收。如果使用大型消息,则可以通过增加此属性来减少临时缓冲区。

尝试增加maxBufferPoolSize以减少内存使用。但是,我强烈建议不要将其最大化,因为来自池的缓冲区直到应用程序域(即应用程序池)回收才会被释放。高流量期可能会导致大量内存被使用且永远不会释放。

1
关于“未管理的内存泄漏”,我曾经遇到过这样的情况,经过一番挖掘,发现是ADO.NET提供程序的问题 - 通过更新版本解决了这个问题 :-)
至于其他方面 - 尝试将此放入配置文件中:
<Configuration>
    <runtime>
        <gcServer enabled=“true“ />
    </runtime>
</Configuration>

除此之外,我认为 WCF 没有什么特别的... 就像任何 .NET 应用程序一样,可能会出现内存泄漏(例如,当事件处理程序和静态处理程序/对象未正确取消订阅时,可能会创建内存泄漏)...


感谢您的反馈。我会尝试这个配置并检查ADO.NET是否会导致任何未受控制的内存泄漏。 - Willy Limarno

0

仅凭直觉:您是否订阅了任何静态事件或长寿对象中的事件?

  • 订阅静态事件的对象将永远存在(或直到取消订阅)。
  • 订阅对象事件的对象将在该对象的生命周期内存在(或直到取消订阅)。

因此,请确保您在这些情况下认真取消订阅事件。


1
一个常见的模式,但不太可能在几分钟内占用8GB。 - H H
@HenkHolterman,在“一般”的客户端应用程序中可能如此。但是,在服务器应用程序尤其是在负载测试下,情况可能会有所不同。 - Branko Dimitrijevic
@branko-dimitrijevic:谢谢,我没有使用太多的事件处理程序(主要在代码中使用接口或委托),所以这不是问题的根本原因。 - Willy Limarno

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接