错过垃圾收集通知

kop*_*nik 5 .net garbage-collection

我们正在使用.NET运行Web场.每个Web服务器在其内存中都包含大量静态对象.Gen 2垃圾收集(GC)需要10-20秒,每5分钟运行一次.我们或多或少地遇到了StackOverflow遇到的相同问题:http://samsaffron.com/archive/2011/10/28/in-managed-code-we-trust-our-recent-battles-with-the- 净垃圾收集器

目前,我们正在减少缓存中的对象数量.但是,这需要时间.

同时,我们实现了此处记录的方法,以便在.NET中获取有关接近GC的通知.目标是在GC接近时将Web服务器从服务器场中取出,并在GC结束后将其包含在服务器场中.但是,我们只收到所有GC的0.7%的通知.我们使用maxGenerationThreshold和largeObjectHeapThreshold为8.我们尝试了其他阈值,但错过的GC数量没有变化.

我们正在使用并发服务器垃圾收集(http://msdn.microsoft.com/en-us/library/ms229357.aspx).GCLatencyMode是Interactive(请参阅http://msdn.microsoft.com/en-us/library/system.runtime.gclatencymode.aspx).在这里,我们再次尝试使用其他GC模式(工作站模式,批处理等).我们再次没有收到大多数GC的通知.

我们做错了什么,或者是否无法获得每个GC的通知?我们如何增加通知数量?

根据http://assets.red-gate.com/community/books/assets/Under_the_Hood_of_.NET_Management.pdf,一开始GC会在Gen2达到~10 MB时触发.我们有很多RAM,所以如果我们可以手动将此阈值设置为更高的级别,则需要更多时间才能达到此阈值,并且根据我的理解,获得通知的概率会增加.有没有办法修改这个阈值?

这是注册和侦听通知的代码:

GC.RegisterForFullGCNotification(gcThreshold, gcThreshold);
// Start a thread using WaitForFullGCProc.
thWaitForFullGC = new Thread(WaitForFullGCProc);
thWaitForFullGC.Name = "HealthTestGCNotificationListenerThread (Threshold=" + gcThreshold + ")";
thWaitForFullGC.IsBackground = true;
Run Code Online (Sandbox Code Playgroud)

WaitForFullGCProc():

    private void WaitForFullGCProc()
{
    try
    {
        while (!gcAbort)
        {
            // Check for a notification of an approaching collection.
            GCNotificationStatus s;
            do
            {
                int timeOut = CheckForMissedGc() > 0 ? 5000 : (10 * 60 * 1000);
                s = GC.WaitForFullGCApproach(timeOut);
                if (this.GcState == GCState.InducedUnnotified)
                {
                    // Set the GcState back to okay to prevent the message from staying in the ApplicationMonitoring.
                    this.GcState = GCState.Okay;
                }
            } while (s == GCNotificationStatus.Timeout);

            if (s == GCNotificationStatus.Succeeded)
            {
                SetGcState(GCState.Approaching, "GC is approaching..");
                gcApproachNotificationCount++;
            }
            else
            {
                ...
            }

            Stopwatch stopwatch = Stopwatch.StartNew();
            s = GC.WaitForFullGCComplete((int)PrewarnTime.TotalMilliseconds);
            long elapsed = stopwatch.ElapsedMilliseconds;

            if (s == GCNotificationStatus.Timeout)
            {
                if (this.ForceGCWhenApproaching && !this.IsInGc && !this.IsPeriodicGcApproaching)
                {
                    this.IsInGc = true;
                    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true);
                    GC.WaitForPendingFinalizers();
                    elapsed = stopwatch.ElapsedMilliseconds;
                    this.IsInGc = false;
                }
            }
        }
        gcAbort = false;
    }
    catch (Exception e)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

Ale*_*lex 4

\n

注意:这更多的是注释,但包含大量代码示例。

\n
\n\n

您是否考虑过尝试通过其他方式获取 GC 通知?Jeffrey Richter(通过 C# 进行 CLR)解释了一种获取通知的好方法,它使用一个对象并检查其终结器方法在哪一代。

\n\n

这是这个类:它使用内部对象,如果提供的一代匹配(参见new GenObject(0);示例),这些对象要么被收集,要么为下一个更高的一代复活。

\n\n

你只需订阅它GCNotification.GCDone += GCDoneHandler;

\n\n
 public static class GCNotification\n    {\n        private static Action<Int32> s_gcDone = null; // The event's field\n        public static event Action<Int32> GCDone\n        {\n            add\n            {\n                // If there were no registered delegates before, start reporting notifications now\n                if (s_gcDone == null) { new GenObject(0); new GenObject(1); new GenObject(2); }\n                s_gcDone += value;\n            }\n            remove { s_gcDone -= value; }\n        }\n        private sealed class GenObject\n        {\n            private Int32 m_generation;\n            public GenObject(Int32 generation) { m_generation = generation; }\n            ~GenObject()\n            { // This is the Finalize method\n                // If this object is in the generation we want (or higher),\n                // notify the delegates that a GC just completed\n                if (GC.GetGeneration(this) >= m_generation)\n                {\n                    Action<Int32> temp = Volatile.Read(ref s_gcDone);\n                    if (temp != null) temp(m_generation);\n                }\n                // Keep reporting notifications if there is at least one delegate registered,\n                // the AppDomain isn't unloading, and the process isn\xe2\x80\x99t shutting down\n                if ((s_gcDone != null)\n                && !AppDomain.CurrentDomain.IsFinalizingForUnload()\n                && !Environment.HasShutdownStarted)\n                {\n                    // For Gen 0, create a new object; for Gen 2, resurrect the object\n                    // & let the GC call Finalize again the next time Gen 2 is GC'd\n                    if (m_generation == 0) new GenObject(0);\n                    else GC.ReRegisterForFinalize(this);\n                }\n                else { /* Let the objects go away */ }\n            }\n        }\n    }\n
Run Code Online (Sandbox Code Playgroud)\n