Страницы

Поиск по вопросам

понедельник, 3 июня 2019 г.

Как отслеживать запуски GC и учитывать их при логировании из разных потоков?

Из разных потоков вызывается метод и надо логировать его работу. Т.к. GC при сборке мусора в WinForms и WPF приложениях может приостановить работу потоков приложения, то это надо учитываться при логировании. Как можно отслеживать запуски GC?


Ответ

Для того чтобы отслеживать GC надо вызвать метод RegisterForFullGCNotification, а также WaitForFullGCApproach и WaitForFullGCComplete
using System.Diagnostics; using System.Collections.Concurrent; using System.Threading; using System.Runtime.CompilerServices;
class LogLine { public int Number; public int ThreadId; public long Ticks; public object Value; public long GCMemory; public int[] GCCollections; public LogLine(int num, object value, long ticks) { this.Number = num; this.Value = value; this.Ticks = ticks; this.ThreadId = Environment.CurrentManagedThreadId; this.GCMemory = GC.GetTotalMemory(false); int[] arr = new int[GC.MaxGeneration + 1]; for (var i = 0; i < arr.Length; i++) arr[i] = GC.CollectionCount(i); this.GCCollections = arr; } }

class Log : IDisposable { Stopwatch sw = Stopwatch.StartNew(); BlockingCollection lines = new BlockingCollection(); void Add(LogLine line) { if (!lines.IsAddingCompleted) lines.Add(line); } public Log() { GC.RegisterForFullGCNotification(1, 1); new Thread(() => { while (!lines.IsAddingCompleted) { Add(new LogLine(-1, GC.WaitForFullGCApproach(), sw.ElapsedTicks)); Add(new LogLine(-2, GC.WaitForFullGCComplete(), sw.ElapsedTicks)); } }).Start(); } public void WriteLine(object value = null, [CallerLineNumber] int cnumber = 0) { Add(new LogLine(cnumber, value, sw.ElapsedTicks)); } void IDisposable.Dispose() { GC.CancelFullGCNotification(); Add(new LogLine(-3, "Disposed", sw.ElapsedTicks)); lines.CompleteAdding(); } public IEnumerable ToCsv() { var s = ",\t "; yield return String.Concat( "Number", s, "Ticks", s, "ThreadId", s, "GCMemory", s, "GCCollections", s, "Value"); foreach (var l in lines) yield return String.Concat( l.Number, s, l.Ticks, s, l.ThreadId, s, l.GCMemory, s, String.Join(";", l.GCCollections), s, l.Value); } }

Для теста в двух потоках создаем и заполняем список массивов по 100 тыс. int
var log = new Log(); using (log) { Parallel.For(0, 2, i => { var lst = new List(); log.WriteLine("new List"); try { while (true) lst.Add(new int[100000]); } catch (OutOfMemoryException) { log.WriteLine("OutOfMemory; lst.Count=" + lst.Count); } }); }
Выводим собранные данные
foreach (var line in log.ToCsv()) Console.WriteLine(line);
Результат (получен в C# Interactive, Microsoft (R) Roslyn C# Compiler version 1.1.0.51204)
Number, Ticks, ThreadId, GCMemory, GCCollections, Value 55, 4030, 6, 6905852, 1;0;0, new List 55, 4426, 9, 6905852, 1;0;0, new List -1, 274370, 11, 1481676908, 6;5;5, Succeeded -2, 274430, 11, 1482877004, 6;5;5, Succeeded -1, 277392, 11, 1528080620, 6;5;5, Succeeded 58, 355462, 6, 1528087152, 8;7;7, OutOfMemory; lst.Count=1888 58, 355462, 9, 1528087152, 8;7;7, OutOfMemory; lst.Count=1920 -2, 317257, 11, 1528087152, 8;7;7, Succeeded -3, 356158, 6, 1528095344, 8;7;7, Disposed

Комментариев нет:

Отправить комментарий