tags:

views:

138

answers:

4

Assume we have a single Windows form with a button called SimpleButton1. The following code results in an uncontrollable amount of memory usage. What am I doing incorrectly?

My understanding is that at every iteration of the for loop, GC cleans up any TestClass objects, and would take care of any associated events as well since there are no handlers on any of the events

Public Class Form1

Private Sub SimpleButton1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SimpleButton1.Click
    For i = 1 To 1000000
        Dim test1 As New TestClass            
    Next
End Sub
End Class

Public Class TestClass
    Private Event TestEvent(ByVal sender As Object)
End Class

Note that I have tried implementing IDisposable and calling test1.Dispose() at the end of every iteration of the For loop, but I suspect I am not disposing the correct resources.

*ANSWERED: There was no problem in the code, and it was run as expected. Problem was that I was running it in debug mode, and the overhead that is created results in large memory usage. See discussion below.

+5  A: 

What you're not showing here and I suspect is the issue is that w/ in your TestClass, you have the event TestEvent and while you're probably adding handlers to TestEvent, you're probably not removing them. By not removing them, you're telling the GC not to collect either TestClass or the handler's class.

James Alexander
Re: JamesI have not added any handlers to TestEvent, with the exception of possible handlers added at compilation or run time. There is nothing I would expect to need removal with the exception of default handlers created this way. Is there anything else in the .NET code I could show you specific to your comment, or is there any way to access handlers added at run time if that is actually the issue?
stacked
James, handlers to TestEvent are __outgoing__ references for the TestClass and will not block GC. Unless instances subscribe to each other.
Henk Holterman
+5  A: 

This code, as you have shown it, will not leak. The garbage collector will eventually take care of the classes you created.

However, if you have an Observable class, and an Observer class, the Observer will call Observable.Event += Observer.EventHandler; By doing this, the Observable now has a reference back to the Observer. If you do not call Observable.Event -= Observer.EventHandler, then that reference stays around.

The problem with this happens when you think nobody has an reference to the Observer, but the Observable has a longer lifespan. Although the Observable has a reference to the Observer, there is no code that knows to remove the reference, unless it clears out all of the handlers (this.Event = null).

This, in effect, is a memory leak!

There are several ways to get around this:

  1. Call -= before you throw out the Observer. Possibly in Dispose()
  2. Use Weak Events
  3. Consider a different eventing pattern, like the Event Aggregator
Brian Genisio
Thank you Brian. You say that "the garbage collector will eventually take care of the classes you created" -- when is "eventually"? If I compile this program and run it, Mem usage in task manager spirals out of control, and is never reclaimed. This eventually leads to an out of memory exception. This would seem to me that GC does not take care of any classes that I create.
stacked
+3  A: 

If you are running under Debug, VisualStudio will create extra code to help with edit and continue ( _EncList stuff), even if you are not under the debugger.

Make sure to compile and run in production compiled as "Release". Yes, this makes it hard to debug memory issues when they behave differently, but it's not the hardest thing about tracking down memory issues.

Philip Rieck
A: 

Thank you for the responses and my apologies for the lack of information provided besides the test app code. I knew it had to be something obvious. I have to make sure I am not creating unnecessary overhead in the debug process. Same sort of thing happened a couple days ago with slow MYSQL queries because I was running an app with a MySQLMonitor object which I could use to track queries with DBMonitor.

stacked