Exemplo n.º 1
0
        private void CollectInfrequently(long privateBytes)
        {
            // not thread-safe, only invoke from timer callback
            Debug.Assert(_inPBytesMonitorThread == 1);

            // The Server GC on x86 can traverse ~200mb per CPU per second, and the maximum heap size
            // is about 3400mb, so the worst case scenario on x86 would take about 8 seconds to collect
            // on a dual CPU box.
            //
            // The Server GC on x64 can traverse ~300mb per CPU per second, so a 6000 MB heap will take
            // about 10 seconds to collect on a dual CPU box.  The worst case scenario on x64 would make
            // you want to return your hardware for a refund.

            long timeSinceInducedGC = DateTime.UtcNow.Subtract(_inducedGCFinishTime).Ticks;
            bool infrequent         = (timeSinceInducedGC > _inducedGCMinInterval);

            // if we haven't collected recently, or if the trim percent is low (less than 50%),
            // we need to collect again
            if (infrequent || _lastTrimPercent < 50)
            {
                // if we're inducing GC too frequently, increase the trim percentage, but don't go above 50%
                if (!infrequent)
                {
                    _lastTrimPercent = Math.Min(50, _lastTrimPercent + 10);
                }
                // if we're inducing GC infrequently, we may want to decrease the trim percentage
                else if (_lastTrimPercent > 10 && timeSinceInducedGC > 2 * _inducedGCMinInterval)
                {
                    _lastTrimPercent = Math.Max(10, _lastTrimPercent - 10);
                }
                int  percent          = (_totalCacheSize > 0) ? _lastTrimPercent : 0;
                long trimmedOrExpired = 0;
                if (percent > 0)
                {
                    Stopwatch sw1 = Stopwatch.StartNew();
                    trimmedOrExpired = _appManager.TrimCaches(percent);
                    sw1.Stop();
                    _trimDurationTicks = sw1.Elapsed.Ticks;
                }

                //

                if (trimmedOrExpired == 0 || _appManager.ShutdownInProgress)
                {
                    return;
                }

                // collect and record statistics
                Stopwatch sw2 = Stopwatch.StartNew();
                GC.Collect();
                sw2.Stop();

                _inducedGCCount++; // only used for debugging
                _inducedGCFinishTime         = DateTime.UtcNow;
                _inducedGCDurationTicks      = sw2.Elapsed.Ticks;
                _inducedGCPostPrivateBytes   = NextSample();
                _inducedGCPrivateBytesChange = privateBytes - _inducedGCPostPrivateBytes;
                // target 3.3% Time in GC, but don't induce a GC more than once every 5 seconds
                // Notes on calculation below:  If G is duration of garbage collection and T is duration
                // between starting the next collection, then G/T is % Time in GC.  If we target 3.3%,
                // then G/T = 3.3% = 33/1000, so T = G * 1000/33.
                _inducedGCMinInterval = Math.Max(_inducedGCDurationTicks * 1000 / 33, 5 * TimeSpan.TicksPerSecond);
                // no more frequently than every 60 seconds if change is less than 1%
                if (_inducedGCPrivateBytesChange * 100 <= privateBytes)
                {
                    _inducedGCMinInterval = Math.Max(_inducedGCMinInterval, 60 * TimeSpan.TicksPerSecond);
                }
#if DBG
                Debug.Trace("CacheMemory", "GC.COLLECT STATS "
                            + "TrimCaches(" + percent + ")"
                            + ", trimDurationSeconds=" + (_trimDurationTicks / TimeSpan.TicksPerSecond)
                            + ", trimmedOrExpired=" + trimmedOrExpired
                            + ", #secondsSinceInducedGC=" + (timeSinceInducedGC / TimeSpan.TicksPerSecond)
                            + ", InducedGCCount=" + _inducedGCCount
                            + ", gcDurationSeconds=" + (_inducedGCDurationTicks / TimeSpan.TicksPerSecond)
                            + ", PrePrivateBytes=" + privateBytes
                            + ", PostPrivateBytes=" + _inducedGCPostPrivateBytes
                            + ", PrivateBytesChange=" + _inducedGCPrivateBytesChange
                            + ", gcMinIntervalSeconds=" + (_inducedGCMinInterval / TimeSpan.TicksPerSecond));
#endif

#if PERF
                SafeNativeMethods.OutputDebugString("  ** COLLECT **: "
                                                    + percent + "%, "
                                                    + (_trimDurationTicks / TimeSpan.TicksPerSecond) + " seconds"
                                                    + ", infrequent=" + infrequent
                                                    + ", removed=" + trimmedOrExpired
                                                    + ", sinceIGC=" + (timeSinceInducedGC / TimeSpan.TicksPerSecond)
                                                    + ", IGCCount=" + _inducedGCCount
                                                    + ", IGCDuration=" + (_inducedGCDurationTicks / TimeSpan.TicksPerSecond)
                                                    + ", preBytes=" + privateBytes
                                                    + ", postBytes=" + _inducedGCPostPrivateBytes
                                                    + ", byteChange=" + _inducedGCPrivateBytesChange
                                                    + ", IGCMinInterval=" + (_inducedGCMinInterval / TimeSpan.TicksPerSecond) + "\n");
#endif
            }
        }