public static void Collect(int generation, GCCollectionMode mode, bool blocking) { if (generation < 0) { throw new ArgumentOutOfRangeException("generation", SR.ArgumentOutOfRange_GenericPositive); } if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Optimized)) { throw new ArgumentOutOfRangeException("mode", SR.ArgumentOutOfRange_Enum); } int iInternalModes = 0; if (mode == GCCollectionMode.Optimized) { iInternalModes |= (int)InternalGCCollectionMode.Optimized; } if (blocking) { iInternalModes |= (int)InternalGCCollectionMode.Blocking; } else { iInternalModes |= (int)InternalGCCollectionMode.NonBlocking; } RuntimeImports.RhCollect(generation, (InternalGCCollectionMode)iInternalModes); }
public static void Collect(int generation, GCCollectionMode mode, bool blocking, bool compacting) { if (generation < 0) { throw new ArgumentOutOfRangeException(nameof(generation), SR.ArgumentOutOfRange_GenericPositive); } if ((mode < GCCollectionMode.Default) || (mode > GCCollectionMode.Aggressive)) { throw new ArgumentOutOfRangeException(nameof(mode), SR.ArgumentOutOfRange_Enum); } int iInternalModes = 0; if (mode == GCCollectionMode.Optimized) { iInternalModes |= (int)InternalGCCollectionMode.Optimized; } else if (mode == GCCollectionMode.Aggressive) { iInternalModes |= (int)InternalGCCollectionMode.Aggressive; if (generation != MaxGeneration) { throw new ArgumentException(SR.Argument_AggressiveGCRequiresMaxGeneration, nameof(generation)); } if (!blocking) { throw new ArgumentException(SR.Argument_AggressiveGCRequiresBlocking, nameof(blocking)); } if (!compacting) { throw new ArgumentException(SR.Argument_AggressiveGCRequiresCompacting, nameof(compacting)); } } if (compacting) { iInternalModes |= (int)InternalGCCollectionMode.Compacting; } if (blocking) { iInternalModes |= (int)InternalGCCollectionMode.Blocking; } else if (!compacting) { iInternalModes |= (int)InternalGCCollectionMode.NonBlocking; } RuntimeImports.RhCollect(generation, (InternalGCCollectionMode)iInternalModes); }
// Garbage collect all generations. public static void Collect() { //-1 says to GC all generations. RuntimeImports.RhCollect(-1, InternalGCCollectionMode.Blocking); }
/// <summary> /// New AddMemoryPressure implementation (used by RCW and the CLRServicesImpl class) /// 1. Less sensitive than the original implementation (start budget 3 MB) /// 2. Focuses more on newly added memory pressure /// 3. Budget adjusted by effectiveness of last 3 triggered GC (add / remove ratio, max 10x) /// 4. Budget maxed with 30% of current managed GC size /// 5. If Gen2 GC is happening naturally, ignore past pressure /// /// Here's a brief description of the ideal algorithm for Add/Remove memory pressure: /// Do a GC when (HeapStart is less than X * MemPressureGrowth) where /// - HeapStart is GC Heap size after doing the last GC /// - MemPressureGrowth is the net of Add and Remove since the last GC /// - X is proportional to our guess of the ummanaged memory death rate per GC interval, /// and would be calculated based on historic data using standard exponential approximation: /// Xnew = UMDeath/UMTotal * 0.5 + Xprev /// </summary> /// <param name="bytesAllocated"></param> public static void AddMemoryPressure(long bytesAllocated) { if (bytesAllocated <= 0) { throw new ArgumentOutOfRangeException("bytesAllocated", SR.ArgumentOutOfRange_NeedPosNum); } #if !BIT64 if (bytesAllocated > Int32.MaxValue) { throw new ArgumentOutOfRangeException("bytesAllocated", SR.ArgumentOutOfRange_MustBeNonNegInt32); } #endif CheckCollectionCount(); uint p = s_iteration % PressureCount; long newMemValue = InterlockedAddMemoryPressure(ref s_addPressure[p], bytesAllocated); Debug.Assert(PressureCount == 4, "GC.AddMemoryPressure contains unrolled loops which depend on the PressureCount"); if (newMemValue >= MinGCMemoryPressureBudget) { long add = s_addPressure[0] + s_addPressure[1] + s_addPressure[2] + s_addPressure[3] - s_addPressure[p]; long rem = s_removePressure[0] + s_removePressure[1] + s_removePressure[2] + s_removePressure[3] - s_removePressure[p]; long budget = MinGCMemoryPressureBudget; if (s_iteration >= PressureCount) // wait until we have enough data points { // Adjust according to effectiveness of GC // Scale budget according to past m_addPressure / m_remPressure ratio if (add >= rem * MaxGCMemoryPressureRatio) { budget = MinGCMemoryPressureBudget * MaxGCMemoryPressureRatio; } else if (add > rem) { Debug.Assert(rem != 0); // Avoid overflow by calculating addPressure / remPressure as fixed point (1 = 1024) budget = (add * 1024 / rem) * budget / 1024; } } // If still over budget, check current managed heap size if (newMemValue >= budget) { long heapOver3 = RuntimeImports.RhGetCurrentObjSize() / 3; if (budget < heapOver3) //Max { budget = heapOver3; } if (newMemValue >= budget) { // last check - if we would exceed 20% of GC "duty cycle", do not trigger GC at this time if ((RuntimeImports.RhGetGCNow() - RuntimeImports.RhGetLastGCStartTime(2)) > (RuntimeImports.RhGetLastGCDuration(2) * 5)) { RuntimeImports.RhCollect(2, InternalGCCollectionMode.NonBlocking); CheckCollectionCount(); } } } } }