diff --git a/Controllers/AppController.cs b/Controllers/AppController.cs index f57f203..dde1182 100644 --- a/Controllers/AppController.cs +++ b/Controllers/AppController.cs @@ -45,6 +45,11 @@ public void AddCustomer(Customer c) { cache.Add(c); } + + public void Clear() + { + cache.Clear(); + } } class Processor @@ -55,6 +60,11 @@ public void ProcessTransaction(Customer customer) { cache.AddCustomer(customer); } + + public void ClearCache() + { + cache.Clear(); + } } [HttpGet] @@ -67,6 +77,9 @@ public ActionResult memleak(int kb) p.ProcessTransaction(new Customer(Guid.NewGuid().ToString())); } + // Clear the cache to prevent memory accumulation + p.ClearCache(); + return "success:memleak"; } @@ -77,18 +90,19 @@ public ActionResult sayhello() return "Hello World!"; } - private static readonly List memoryHog = new(); - [HttpGet] [Route("crash")] public ActionResult crash() { + // Use local variable instead of static to allow garbage collection + var localMemoryHog = new List(); double bytesSize = 0; while (true || bytesSize < 1_000_000) { bytesSize += 10 * 1024 * 1024; // 10MB - memoryHog.Add(new byte[10 * 1024 * 1024]); // Allocate 1MB + localMemoryHog.Add(new byte[10 * 1024 * 1024]); // Allocate 10MB (comment was incorrect) } + // localMemoryHog will be eligible for GC when method exits return "success:oomd"; } diff --git a/Program.cs b/Program.cs index 2d9aa50..736f032 100644 --- a/Program.cs +++ b/Program.cs @@ -27,7 +27,8 @@ public static void Main(string[] args) app.UseAuthorization(); app.MapControllers(); - Subscriber.CreatePublishers(); + // Removed Subscriber.CreatePublishers() to prevent startup memory leak + // This can be called via the /api/app/appinvoke endpoint if needed app.Run(); } diff --git a/PublisherSubscriber.cs b/PublisherSubscriber.cs index eba63a5..27b4753 100644 --- a/PublisherSubscriber.cs +++ b/PublisherSubscriber.cs @@ -2,7 +2,7 @@ { public class Publisher { - public event EventHandler SomeEvent; + public event EventHandler? SomeEvent; public void RaiseEvent() { @@ -10,27 +10,52 @@ public void RaiseEvent() } } - public class Subscriber + public class Subscriber : IDisposable { private readonly byte[] _largeData = new byte[1024 * 1024]; // 1MB of data + private Publisher? _publisher; public void Subscribe(Publisher publisher) { - publisher.SomeEvent += OnEvent; // Never unsubscribed + _publisher = publisher; + publisher.SomeEvent += OnEvent; } - private void OnEvent(object sender, EventArgs e) + public void Unsubscribe() + { + if (_publisher != null) + { + _publisher.SomeEvent -= OnEvent; + _publisher = null; + } + } + + private void OnEvent(object? sender, EventArgs e) { Console.WriteLine($"Event received, data size: {_largeData.Length}"); } + public void Dispose() + { + Unsubscribe(); + } + public static Publisher CreatePublishers() { var publisher = new Publisher(); + var subscribers = new List(); + for (int i = 0; i < 2_100; i++) { var subscriber = new Subscriber(); subscriber.Subscribe(publisher); + subscribers.Add(subscriber); + } + + // Clean up subscribers to prevent memory leak + foreach (var subscriber in subscribers) + { + subscriber.Dispose(); } return publisher;