简体   繁体   English

Windows 服务 memory 泄漏使用 TopShelf 和 Automapper c#

[英]Windows service memory leak using TopShelf and Automapper c#

I'm running a windows service using TopShelf (based on console app in C# .NET 4.6.1) and I'm using Automapper 9.0.0.我正在使用 TopShelf 运行 windows 服务(基于 C# .NET 4.6.1 中的控制台应用程序)并且我正在使用 Automapper 9.00。 Every 10 seconds I run a task that processes about 1000 rows in a Ms SQL database (using entity framework), It seems like Automapper is taking up a lot of memory, and the memory grows each time the task is run (In task manager I can see the service taking up over 3000 Meg of RAM++). Every 10 seconds I run a task that processes about 1000 rows in a Ms SQL database (using entity framework), It seems like Automapper is taking up a lot of memory, and the memory grows each time the task is run (In task manager I可以看到该服务占用了超过 3000 Meg 的 RAM++)。

I am new to Automapper and don't now if there is anything I need to code to release manually the memory.我是 Automapper 的新手,如果我需要编写任何代码来手动发布 memory,我现在不需要。 Somewhere I saw a huge amount of handlers and I was wondering if Automapper generates these handlers and how I can clean them up.在某处我看到了大量的处理程序,我想知道 Automapper 是否会生成这些处理程序以及如何清理它们。

I tried putting a GC.Collect() at the end of each task but I don't seem to see a difference我尝试在每个任务的末尾放置一个GC.Collect()但我似乎看不出有什么不同

在此处输入图像描述

Here is a code extract of my task:这是我的任务的代码摘录:

private void _LiveDataTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            // setting up Ninject data injection
            var kernel = new StandardKernel();
            kernel.Load(Assembly.GetExecutingAssembly());
            //var stationExtesions = kernel.Get<IStationExtensionRepository>();
            //var ops = kernel.Get<IOPRepository>();
            //var opExtensions = kernel.Get<IOPExtensionRepository>();
            //var periods = kernel.Get<IPeriodRepository>();
            //var periodExtensions = kernel.Get<IPeriodExtensionRepository>();


            // create the LiveDataTasks object
            //var liveDataTasks = new LiveDataTasks(stationExtesions, ops, opExtensions, periods, periodExtensions);

            // sync the station live data
            //liveDataTasks.SyncLiveStationData();

            // force garbage collection to prevent memory leaks
            //GC.Collect();

            Console.WriteLine("LiveDataTimer: Total available memory before collection: {0:N0}", System.GC.GetTotalMemory(false));
            System.GC.Collect();
            Console.WriteLine("LiveDataTimer: Total available memory collection: {0:N0}", System.GC.GetTotalMemory(true));
        }

MOFICATIONS: I added some console outputs at the end of the code displaying the TotalMemory used. MOFICATIONS:我在显示使用的 TotalMemory 的代码末尾添加了一些控制台输出。 I removed GC.Collect() because it doesn't change anything and commented out most of the code accessing database.我删除了GC.Collect()因为它没有改变任何东西并注释掉了大部分访问数据库的代码。 Now I realize that kernel.Load(Assembly.GetExecutingAssembly());现在我意识到kernel.Load(Assembly.GetExecutingAssembly()); already makes memory grow very fast.已经使 memory 增长非常快。 See the following console capture:请参阅以下控制台捕获: 在此处输入图像描述 Now if I comment out kernel.Load(Assembly.GetExecutingAssembly());现在,如果我注释掉kernel.Load(Assembly.GetExecutingAssembly()); I get a stable memory situation again.我再次得到稳定的 memory 情况。 How can I Dispose or unload the Kernel???如何处置或卸载 Kernel? 在此处输入图像描述

Well, first of all you should not be doing Database work in a Service.好吧,首先你不应该在服务中做数据库工作。 Moving any big operation on DB data out of the DB will only add having to move the data over the Network twice - once to the client programm, once back to the DB - while also risking Race Conditions and a lot of other issues.将数据库数据上的任何大型操作移出数据库只会增加必须通过网络将数据移动两次——一次到客户端程序,一次回到数据库——同时还冒着竞争条件和许多其他问题的风险。 My standing advice is: Keep DB work in the DB at all times.我的长期建议是:始终保持数据库在数据库中工作。

As for the memory Footprint, this migth just be a missreading of the used Memory:至于 memory 封装,这可能只是对使用过的 Memory 的误读:

.NET uses the Garbage Collection Memory Management approach. .NET 使用垃圾收集 Memory 管理方式。 One effect of it is that while the GC for any given Application does his collecting, all other threads have to be paused.它的一个效果是,当任何给定应用程序的 GC 进行收集时,所有其他线程都必须暂停。 As a result the GC is pretty lazy at running.结果,GC 非常懒惰地运行。 If it only runs once on Application closure - that is the ideal case.如果它只在应用程序关闭时运行一次 - 这是理想的情况。 So it tries avoding to run before that unessesarily.所以它试图避免不必要地在那之前运行。 It will still run as much as it can, before it ever throws a OutOfMemoryException at you.在向您抛出 OutOfMemoryException 之前,它仍会尽可能多地运行。 But beyond that, it is perfectly happy to just keep allocating more and more object without cleaning up.但除此之外,很高兴在不清理的情况下继续分配越来越多的 object。

You can test if it is that by calling GC.Collect().您可以通过调用 GC.Collect() 来测试是否是这样。 However such a call should generally never be in productive code.然而,这样的调用通常不应该在生产代码中。 A alternate GC strategy (particular the one used for WebServers) might be better.另一种 GC 策略(特别是用于 WebServers 的策略)可能会更好。

I finally figured out what was happening: the kernel.Load(...) used to set up NInject data injection was increasing my memory:我终于弄清楚发生了什么:用于设置NInject 数据注入kernel.Load(...)正在增加我的 memory:

var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());

So I moved this code from the function executed every x seconds to the constructor of the parent class where it is only executed once on initialisation.因此,我将此代码从每 x 秒执行一次的 function 移动到父 class 的构造函数,其中它仅在初始化时执行一次。

This solved the problem.这解决了问题。

Thanks guys for your inspiring help and comments!!!!谢谢大家的鼓励和评论!!!!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM