Кстати, можно ли смоделировать эту проблему/задачу в относительно маленьком куске кода для последующих экспериментов/бенчмарков?
Конечно можно. Я так и делал. Запускаешь два потока. Один поток мусорит. Второй поток делает Sleep(1) и измеряет сколько времени он на самом деле проспал, максимальное время спячки и будет максимальной паузой GC.
Программа работает очень долго, для построения одной "линии" я оставлял её на ночь:
namespace TestGCPause
{
class Program
{
static void Main (string[] args)
{
using (OneTest x = new OneTest())
{
for (uint i = 1; i < 100; i++)
// вычисляем 100 точек для одной "линии" {
System.GC.Collect();
x.Run((1000000 * i)*4);
}
}
}
}
public sealed class OneTest: System.IDisposable
{
private bool isDisposed;
private double maxPause;
public OneTest ()
{
System.Threading.Thread thread = new System.Threading.Thread(this.GCLoop);
thread.Start();
}
public void Run (uint objectCount)
{
this.maxPause = 0;
long m0 = System.GC.GetTotalMemory(true);
this.maxPause = 0;
byte[][] a = new byte[objectCount][];
ulong k = 127837817;
// это быстрый генератор случайных чисел const ulong dk = 3746123567;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < objectCount; j++)
{
k = k + dk;
ulong size = 16 + (k % (64 - 16));
//ulong size = 64 + (k % (256 - 64)); //ulong size = 256 + (k % (1024 - 256)); byte[] x = new byte[size];
x[k % size] = (byte)k;
a[k % objectCount] = x;
}
}
long m1 = System.GC.GetTotalMemory(true);
System.Console.Write("{" + (m1 - m0) + ", " + this.maxPause + "},");
System.GC.KeepAlive(a);
}
private void GCLoop ()
// Этот поток измеряет паузы {
while (!this.isDisposed)
{
System.DateTime t0 = System.DateTime.UtcNow;
System.Threading.Thread.Sleep(1);
System.DateTime t1 = System.DateTime.UtcNow;
double dt = (t1 - t0).TotalSeconds;
if (this.maxPause < dt)
{
this.maxPause = dt;
}
}
}
public void Dispose ()
{
this.isDisposed = true;
}
}
}