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
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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|