I will be demonstrating this in multiple steps which include identification of memory leaks and then moving into the details of how to find those references which are not released by using the dump file.
In step 1, I will create a .NET console application to have logic which has memory leaks and then will identify via 'performance monitor' a default Windows tool to detect and finally, I'll use a dump file to find out all the unrelased references.
Here is a code snippet of the application "memoryleakdemo' which has memory leaks. It opens 10K instances of file stream but none is disposed of.
- class Program
- {
- static void Main(string[] args)
- {
- IList<Company<Employee>> companies = new List<Company<Employee>>();
- var location = new Uri(Assembly.GetEntryAssembly().CodeBase).LocalPath;
- var path = Path.GetDirectoryName(location);
- var filePath = args.Any() ? args[0] : Path.Combine(path, "Resource\\Companydata.txt");
- var mainStream = new StreamWriter(filePath);
- var i = 0;
- // 1. memory leak : Create steam for each company but not disposed.
- // 2. event subscribed not released.
- while (i++ <= 10000)
- {
- foreach (var item in new List<string> { "DX Generation", "Nxt", "Dudley Boyz" })
- {
- var company = new Company<Employee>(item);
- company.OnNewJoinne += e =>
- {
- CompanyOnNewJoinne(e, mainStream);
- };
- foreach (var item1 in new List<string> { "employe1", "employe2" })
- {
- company.AddNewEmployee(item1);
- }
- companies.Add(company);
- }
- }
- mainStream.Dispose();
- Console.ReadKey();
- }
- private static void CompanyOnNewJoinne(EmployeeJoinedEventArgs e, StreamWriter stream)
- {
- Console.WriteLine($"{e.EmployeeName} employee Joined {e.EmployerName}");
- stream.WriteLine($"{e.EmployeeName} employee Joined {e.EmployerName}");
- }
- }
- public class Company<T> where T : Employee
- {
- public delegate void EmployeJoined(EmployeeJoinedEventArgs name);
- public event EmployeJoined OnNewJoinne;
- public string Name { get; private set; }
- public StreamWriter FileStream { get; private set; }
- private readonly IList<T> employee = new List<T>();
- public Company(string name)
- {
- this.Name = name;
- var location = new Uri(Assembly.GetEntryAssembly().CodeBase).LocalPath;
- var path = Path.GetDirectoryName(location);
- // each object have fileStream but no dispose.
- // you can fix it via Idispose implementation to dispose FileStream
- this.FileStream = File.CreateText(Path.Combine(path, Path.GetRandomFileName() + ".txt"));
- }
- public bool AddNewEmployee(string name)
- {
- if(employee.FirstOrDefault(s => s.Name == name) != null)
- {
- return false;
- }
- employee.Add((T)Activator.CreateInstance(typeof(T), name));
- // Write in local stream.
- FileStream.WriteLine($"{name} employee Joined {this.Name}");
- // write in main stream.
- OnNewJoinne?.Invoke(new EmployeeJoinedEventArgs(this.Name, name));
- return true;
- }
- }
- public class Employee
- {
- public string Name { get; private set; }
- public Employee(string name)
- {
- this.Name = name;
- }
- }
- public class EmployeeJoinedEventArgs : EventArgs
- {
- public EmployeeJoinedEventArgs(string companyName, string employeeName)
- {
- EmployerName = companyName;
- EmployeeName = employeeName;
- }
- public string EmployerName { get; private set; }
- public string EmployeeName { get; private set; }
- }





Abhay DeshpandePosted Oct 10, 2019, 12:49 AM
Nice article
Mahesh ChandPosted Oct 8, 2019, 2:18 PM
Thank you, Ashutosh. Appreciate your detailed work.
Sourav Kumar DasPosted Oct 8, 2019, 1:28 AM
Nice Article.