You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

256 lines
8.2 KiB

using System;
using System.Collections.Generic;
using System.Runtime;
namespace Sog
{
public class GCMonitor : Singleton<GCMonitor>
{
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;
}
/// <summary>
/// 可以在凌晨的时候调用一下,整理内存,注意不同进程分开执行,免得整个服务器卡住
/// 不知道效果如何,上线后可以测试一下
/// </summary>
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<GCTest>
{
List<GCTestClassNumObj> classList = new List<GCTestClassNumObj>();
Queue<GCTestClassNumObj> memList = new Queue<GCTestClassNumObj>();
List<string> stringList = new List<string>();
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();
}
}
}
}
}