Утечка памяти в службе Windows System.Threading.Timer - PullRequest
0 голосов
/ 01 июня 2018

У меня консольное приложение ac #, работающее как служба Windows.Основной поток приложения использует System.Threading.Timer для управления «развертками» базы данных.Когда развертка начинается при отметке таймера, таймер создает объект для поиска работы в базе данных, и этот объект вызывает событие, когда обнаруживается, что работа порождает задание в другой поток.

Кажется, я страдаюутечка памяти, и мои ограниченные способности windbg, запрашивающие дампы памяти, заставляют меня думать, что таймер не высвобождает что-либо в GC.

windbg:

!dumpheap -type System.Object[]
...
02b01ae0 73a20cbc    32656   
02b09a80 73a20cbc     4112     
02b0e8e0 73a20cbc    16336     
02b5bf88 73a20cbc     1040     
02b5c3a8 73a20cbc     2064
10ee1010 73a20cbc 268435472     

Statistics:
      MT    Count    TotalSize Class Name
04210964        1           32 System.Func`2[[System.Type, mscorlib],[System.Func`2[[System.Object[], mscorlib],[Newtonsoft.Json.JsonConverter, Newtonsoft.Json]], mscorlib]]
7366a6a4        1           48 System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Object[], mscorlib]]
04210a5c        1           48 System.Collections.Generic.Dictionary`2[[System.Type, mscorlib],[System.Func`2[[System.Object[], mscorlib],[Newtonsoft.Json.JsonConverter, Newtonsoft.Json]], mscorlib]]
73a20cbc     1194    268556916 System.Object[]
Total 1198 objects
0:000> !gcroot 10ee1010 
Thread 2dfc:
    04f1f70c 72db0687 System.Net.TimerThread.ThreadProc()
        ebp+50: 04f1f710 (interior)
            ->  02ac33a8 System.Object[]
            ->  072a5a38 System.Net.ServerCertValidationCallback
            ->  072a5a18 System.Net.Security.RemoteCertificateValidationCallback
            ->  10ee1010 System.Object[] 

Упрощенно и сжатоcode:

TimeSpan timerInterval = TimeSpan.FromSeconds(5);
static Timer t;
public void startTimer()
{
    t = new System.Threading.Timer(TimerTick, null, TimeSpan.Zero, timerInterval);
}
public void TimerTick(Object TimerState)
{
    //run each query sweep synchronously
    t.Change(Timeout.Infinite, Timeout.Infinite);
    List<Query> queries = GetQueries();
    foreach (var query in queries)
    {
        var search = new QueryProcessor(query);
        //short term publisher?
        search.resultFoundEvent += QueryResultEventListener;
        search.RunSearch();
    }
    t.Change(timerInterval, timerInterval);
}
public void RunJob(JobDetails job)
{
    Task.Factory.StartNew(() => job.Execute(JobCallback));
}
//long term subscriber
public void QueryResultEventListener(object sender, FakeEventArgs e)
{
    RunJob(e.jobdetails);
}
public void JobCallback(JobDetails jobsuccess)
{
    //job was completed
}

Как уже отмечалось выше, я не думаю, что это утечка традиционного обработчика событий, а у windbg выше есть только одна массивная запись внизу !dumpheap -stat для System.Object[].

1 Ответ

0 голосов
/ 14 июня 2018

Используя jetBrains dotMemory, я смог определить, что открытый дескриптор был из строки кода, вызываемой из ссылочного управляемого кода вниз по стеку в job.Execute().Он устанавливал делегата глобально и заставлял ServerCertificateValidationCallback закреплять дескриптор и никогда не отпускать.

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => { }; Такое ощущение, что он ведет себя в классической моде утечки памяти «краткосрочный издатель, долгосрочный подписчик», поскольку вскоре после этого был удален родительский объект конструктора, вызывающего эту строку.

...