1

我有一个在 .NET Framework 3.5 上运行的 C# Windows 服务,它显示出不断增长的 GC 句柄(使用 Windows Server 2003 上的系统监视器查看)。

我确保所有资源都被正确处理,并且我的代码中没有终结器。

“Large Object Heap size”和“# Bytes in all Heaps”相对而言是静态的,我可以看到“% Time in GC”表明正在发生垃圾收集。

“Private Bytes”计数器也在增加。

这种症状导致我在任务管理器中的“内存使用量”以每天大约 35 MB 的速度增长,这是不可接受的,因为该服务基本上是针对 Oracle 10g 运行一个简单的 SELECT 查询并每 5 秒使用 .NET TraceSources。值得一提的是,TraceSource 使用 .NET 侦听器对象输出到 Windows 事件日志和文本文件。

有谁知道为什么“# GC Handles”不断增加,因为我相信这与我的“内存使用”增加有关?

4

6 回答 6

2

好的 - 排序它。

在检查 .NET TraceSource 类的源代码后,它似乎在设计上拥有 2 个 WeakReferences 列表 - 1 个用于 TraceSource 对象,1 个用于 Switches。

它这样做是为了支持其 RefreshAll 方法。

我不得不使用反射手动清除这些列表。所以,我不能再安全地使用 RefreshAll 方法,但至少我的 Private Bytes 和 GC Handles 不再增长。

要重现该问题,只需创建一整套 TraceSource 对象并关闭它们 - 您可以看到“泄漏”。

于 2009-02-27T09:05:40.983 回答
1

您没有释放代码正确引用的非托管资源。

你熟悉固定语句吗?它可以固定内存,以便以不安全的方式访问它。但是,还有另一种方法可以做我们认为可能不安全的事情。

var handle = System.Runtime.InteropServices.GCHandle.Alloc(myObject,
        System.Runtime.InteropServices.GCHandleType.Pinned);

上述类型的代码正是可能导致您的问题类型的原因。如果你没有明确地'.Free()'这个固定的内存,你最终不会垃圾收集这个对象,你会有内存泄漏。

我的猜测是,您的 Oracle 10g 提供程序正在发生类似的事情,除非您知道您正在做其他可能会泄漏内存的事情。

于 2009-02-25T08:38:37.647 回答
1

对于它的价值,我们已经在 .NET 4.0 中为 TraceSource 和 Switch 修复了这个问题。

我们仍然认为最好的做法是不创建一堆不同的 TraceSource 实例(而是为每个源创建一个静态实例并跨线程共享它们),但在某些情况下,如果您使用第三方库,这是不可能的你无法修复。

于 2009-03-20T07:17:02.690 回答
0

你在使用 ODP.Net 吗?请记住,ODP.Net 不是完全托管的,并且也有一个本地组件。您是否使用任何其他本机资源,例如 ActiveDirectory 等。请记住,即使您有 .Net 程序集,它也是 COM 上的一个薄层,用于 AD 之类的东西。我在我使用的 AD 代码中看到了大量的内存泄漏。不过,我的 ODP.Net 没有内存问题,而且我公司的企业服务使用 ODP.Net 运行。

于 2009-02-25T08:52:09.720 回答
0
private void TidyWeakReferences(System.Diagnostics.TraceSource source) {

        try {
            // Clear down the Switch's WeakReferences
            TidyList(source, typeof(System.Diagnostics.Switch).GetFields(BindingFlags.NonPublic | BindingFlags.Static));
            // Clear down the Source's WeakReferences
            TidyList(source, typeof(System.Diagnostics.TraceSource).GetFields(BindingFlags.NonPublic | BindingFlags.Static));
        }
        catch { /* Nothing we can do here */ }
    }

private void TidyList(System.Diagnostics.TraceSource source, FieldInfo[] info) {

        List<WeakReference> list = null;

        foreach (FieldInfo fi in info) {

            if (fi.Name == "switches" | fi.Name == "tracesources") {

                list = (List<WeakReference>)fi.GetValue(null);
                lock (list) {
                    for (int i = list.Count - 1; i >= 0; i--) {
                        // Check to see if the GC has already collected these objects
                        if (!list[i].IsAlive) {
                            // It's dead, so remove it from the List (as .NET Framework 4.0 SHOULD fix.)
                            list.RemoveAt(i);
                        }
                    }
                }
            }
        }
于 2010-04-06T12:17:12.800 回答
0

这个调试体验的链接应该添加到这个讨论中。它补充了@thehowler 发布的解决方法。

http://www.wintellect.com/blogs/jrobbins/is-that-a-weakreference-in-your-gen-2-or-are-you-just-glad-to-see-me

基本上, TraceSource 的每个新分配都会添加到未进行垃圾收集的 WeakReference 列表中。

于 2014-11-20T04:08:05.257 回答