using System; using System.Collections.Generic; using System.Runtime; namespace Sog { public class GCMonitor : Singleton { long m_lastTickTimeMs = 0; //gc的花费时间,毫秒 int m_totalGCCount = 0; long m_totalGCTimeMs = 0; long m_GCTimeMsMax = 0; //一次long之间的最大 long m_GCTimeMsMaxInterval = 0; //总共gc数量 long m_totalGC0Count = 0; long m_totalGC1Count = 0; long m_totalGC2Count = 0; //一段时间内的gc数量 long m_lastGC0Count = 0; long m_lastGC1Count = 0; long m_lastGC2Count = 0; private long m_maxTimeTick; private long m_totalTickCount; private long m_totalTickTime; private int m_scriptTickCount; // 不要trace gc, sm服务器管理工具没有替换protobuf, 消息收发会导致gc log太频繁, 影响日志查看 private int noTraceGC1; private int noTraceGC2; public GCMonitor() { //System.GC.Concurrent //设置成LowLatency和SustainedLowLatency会有后遗症,导致fullgc的时候更慢 GCSettings.LatencyMode = GCLatencyMode.Interactive; GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.Default; TraceLog.Debug("GCMonitor Init, IsServerGC {0} LatencyMode {1} LargeObjectHeapCompactionMode {2}", GCSettings.IsServerGC, GCSettings.LatencyMode, GCSettings.LargeObjectHeapCompactionMode); } public void ScriptOnTick() { m_scriptTickCount++; } public void CloseLog(int gcLv) { noTraceGC1 = gcLv >= 1 ? 1 : 0; noTraceGC2 = gcLv >= 2 ? 1 : 0; } public void Tick() { //var lmode = System.Runtime.GCSettings.LatencyMode; //var hc = System.Runtime.GCSettings.LargeObjectHeapCompactionMode; long tNowMs = AppTime.GetNowSysMs(); if (m_lastTickTimeMs == 0) { m_lastTickTimeMs = tNowMs; } long tickTime = tNowMs - m_lastTickTimeMs; if (tickTime > m_maxTimeTick) { m_maxTimeTick = tickTime; } m_totalTickTime += tickTime; m_totalTickCount++; int count0 = GC.CollectionCount(0); int count1 = GC.CollectionCount(1); int count2 = GC.CollectionCount(2); //0代就算了,太多了 if ((noTraceGC2 == 0 && count2 != m_totalGC2Count) || (noTraceGC1 == 0 && count1 != m_totalGC1Count) || tickTime > 100) { m_totalGCCount++; m_totalGCTimeMs += tickTime; if (m_GCTimeMsMax < tickTime) { m_GCTimeMsMax = tickTime; } if (m_GCTimeMsMaxInterval < tickTime) { m_GCTimeMsMaxInterval = tickTime; } TraceLog.Debug("GCMonitor.OnGCComplete use time {0} ms, Mem {1} gc1 {2} gc2 {3}" , tickTime, GC.GetTotalMemory(false), count1, count2); } m_totalGC2Count = count2; m_totalGC1Count = count1; m_totalGC0Count = count0; m_lastTickTimeMs = tNowMs; } public void LogStat() { long last0 = m_lastGC0Count; long last1 = m_lastGC1Count; long last2 = m_lastGC2Count; m_lastGC0Count = m_totalGC0Count = GC.CollectionCount(0); m_lastGC1Count = m_totalGC1Count = GC.CollectionCount(1); m_lastGC2Count = m_totalGC2Count = GC.CollectionCount(2); TraceLog.Stat("GCMonitor TickCount {0} MaxTickTime {1} AvgTickTime {2} ScriptTickCount {3}" , m_totalTickCount, m_maxTimeTick, m_totalTickCount == 0 ? 0 : m_totalTickTime / m_totalTickCount, m_scriptTickCount); string strMessage = string.Format("GCCount gen0 {0}({1}) gen1 {2}({3}) gen2 {4}({5}), max gc time {6}({7}) avg {8}" , m_totalGC0Count - last0, m_totalGC0Count, m_totalGC1Count - last1, m_totalGC1Count, m_totalGC2Count - last2, m_totalGC2Count , m_GCTimeMsMaxInterval, m_GCTimeMsMax, m_totalGCCount == 0 ? 0 : m_totalGCTimeMs / m_totalGCCount); TraceLog.Stat("GCMonitor {0}", strMessage); TraceLog.Stat("{0}", Sog.Memory.ByteArrayCacheMgr.Instance.StatInfo()); m_GCTimeMsMaxInterval = 0; m_maxTimeTick = 0; m_totalTickTime = 0; m_totalTickCount = 0; m_totalGCTimeMs = 0; m_totalGCCount = 0; m_scriptTickCount = 0; } /// /// 可以在凌晨的时候调用一下,整理内存,注意不同进程分开执行,免得整个服务器卡住 /// 不知道效果如何,上线后可以测试一下 /// public void FullGC() { //这个CompactOnce会在fullgc之后自动改成default GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; GC.Collect(); } } internal class GCTestClassNumObj { public string name; public byte[] buffer; } /* 测试结果,对象数量上升到1千万,fullgc一下的时间已近超过1秒,已近很卡了 所以服务器的对象数量需要控制在百万以内,根据测试结果,string对象也是对象,影响gc时间,和一般的对象没什么本质区别 */ //测试一下小内存,结论:内存大小影响gc相对较小,对象数量才是,不过内存过大(系统内存不足)容易引起频繁的fullgc //小对象过多加上碎片过多的情况,在2代gc的时候触发整理,小对象占用内存较大的时候还是会比较卡的,本质还是数量太多 //所以运营期的服务器要保证物理内存的充足 internal class GCTest : Singleton { List classList = new List(); Queue memList = new Queue(); List stringList = new List(); public void TestMemoryNew() { //对象数量,数量变的时候,对象大小会发生改变,用来确定内存大小一定的时候,对象数量多少对gc的影响 int iObjectCount = 20000; long memorySize = 5000000000; //2G内存 int buffsize = (int)(memorySize / iObjectCount); //测试gc,对象数量的影响,new的影响 if (classList.Count < iObjectCount) { for (int i = 0; i < 100; i++) { byte[] newbuffer = new byte[buffsize]; for(int j=0; j< newbuffer.Length; j++) { newbuffer[j] = 255; } classList.Add(new GCTestClassNumObj() { name = classList.Count.ToString() , buffer = newbuffer }); //GC.KeepAlive } } //1千万个string对象 if(stringList.Count < 10000000) { for (int i = 0; i < 100; i++) { //stringList.Add( stringList.Count.ToString()); } } for (int i = 0; i < 100; i++) { int randLen = new Random().Next(100, 1000); byte[] newArray = new byte[randLen]; for (int j = 0; j < newArray.Length; j++) { newArray[j] = (byte)(j + i); } GCTestClassNumObj obj = new GCTestClassNumObj(); obj.buffer = newArray; obj.name = newArray.Length.ToString(); memList.Enqueue(obj); } if (memList.Count > 1000000) { for (int i = 0; i < 1000; i++) { memList.Dequeue(); } } } } }