I noticed that my classic .net web application would leak memory after I implemented metrics for some background tasks.
Further investigation showed that my MetricAggregationManager would not release its memory.
Since one of the major changes was the implementation of a TelemetryClient, and since the memory not being released was from the Microsoft.ApplicationInsights.Metrics namespace, I concluded that the problem lies within the creation of the TelemetryClient:
using System; using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.Metrics; namespace MyCode { public class BaseProcessor { private readonly TelemetryClient _telemetryClient; private BaseProcessor() { string instrumentationKey = "somekey" var telemetryConfiguration = new TelemetryConfiguration { InstrumentationKey = instrumentationKey }; // This is a no-go. I should not create a new instance for every BaseProcessor _telemetryClient = new TelemetryClient(telemetryConfiguration); } } }
The code above will create a new TelemetryClient for each creation of my base class. The TelemetryClient will collect metrics and store those in memory until either a set time or number of metrics are met, and then dump the metrics to Application Insights.
So when the BaseClass is disposed, TelemetryClient is not, leaving memory to hang, and thus a memory leak is in effect.
HOW TO SOLVE IT?
The solution is simple. All you need to do is to create a singleton pattern for your TelemetryClient. Having only one instance will allow the client to collect and send metrics in peace. Your code will be much faster (it takes a millisecond or so to create a TelemetryClient) and you will not have any memory leaks.
USE DEPENDENCY INJECTION:
In .NET Core you can add the TelemetryClient to the service collection:
private static void ConfigureServices(IServiceCollection services) { // Add Application Insights var telemetryConfiguration = TelemetryConfiguration.CreateDefault(); telemetryConfiguration.InstrumentationKey = "somekey" var telemetryClient = new TelemetryClient(telemetryConfiguration); services.AddSingleton(telemetryClient); }
And then reference it using constructor injection:
using System; using System.Runtime.Serialization; using Microsoft.ApplicationInsights; using Microsoft.AspNetCore.Mvc; namespace MyCode { [ApiController] [Route("/api/[controller]")] [Produces("application/json")] public class MyController : ControllerBase { private readonly TelemetryClient _telemetryClient; public MyController(TelemetryClient telemetryClient) { _telemetryClient = telemetryClient; } } }
USE A STATIC VARIABLE:
If you do not have access to a DI framework, you could also just create a static variable:
using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.Extensibility; using System.Collections.Generic; namespace MyCode { public static class TelemetryFactory { private static TelemetryClient _telemetryClient; public static TelemetryClient GetTelemetryClient() { if (_telemetryClients == null) { string instrumentationKey = "somekey"; var telemetryConfiguration = new TelemetryConfiguration { InstrumentationKey = instrumentationKey }; _telemetryClient = new TelemetryClient(telemetryConfiguration); } return _telemetryClient; } } }
And then reference the static variable instead:
using System; using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.Metrics; namespace MyCode { public class BaseProcessor { private readonly TelemetryClient _telemetryClient; private BaseProcessor() { _telemetryClient = TelemetryFactory.GetTelemetryClient(); } } }
MORE TO READ:
- Application Insights API for custom events and metrics from Microsoft
- Implementing the Singleton Pattern in C# from C# In Depth
- Azure ApplicationInsights track custom metrics using the TelemetryClient from briancaos
Brian, Thanks for the great article. I stumbled upon this while searching for AppInsights memory leaks. I think I have similar implementation but I am still leaking memory in the long running background tasks. I am using Simple Injector DI in .NET web api (.net 4.8 framework) and have a wrapper class(my own implementation) around the telemetry client.
public class MyAppInsights : IMyAppInsights
{
private static TelemetryClient telemetry;
public MyAppInsights ()
{
if (telemetry == null)
{
telemetry = new TelemetryClient();
}
}
//other interface method implementations
}
simple injector startup configuration
container.Register(Lifestyle.Singleton);
Memory leak problem appeared only recently after implementing a long running background task. While profiling I noticed that appinsights was holding on to httpwebrequest objects in the memory and not disposing them .
Any pointers in fixing this would be appreciated
Thanks
LikeLiked by 1 person
Thanks your entry. We also had a memory leak because TelemtryClient was being instantiated per request in an Azure Function.
LikeLike
Pingback: Using Azure Functions to Run Web Availability Tests - Krueger Web Design