Memory usage - events

I recently answered a question at VBCity about a memory leakage with VB.Net:

"I am creating a class with an Event, then I instantiate the class and dispose of it. VB seems to be wasting 16 bytes each time. "

I did some research, and found something really interisting. A small VB.Net application to test this showed that the memory usage slowly increases over time. The object never gets collected. A C# test worked as expected (memory usage increases the 1st time an object is created, and then it stays the same).

When I investigated the CLR code for this, I noticed why: C#'s ctor function only created the object, while VB's did the following:

IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0007: ldsfld class [mscorlib]System.Collections.ArrayList ConsoleApplication1.LeakingClass::__ENCList
IL_000d: newobj instance void [mscorlib]System.WeakReference::.ctor(object)
IL_0012: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)

It seems to me as if it is adding the object to an arraylist (only if you have an event declaration in your class).

Is this a bug, or was it done for a reason (my best guess would be that it was done in order for other objects to still be able to respond to events raised by this object, even after it was disposed)? And if it was done for a reason, is there a work-around (other than using C#)?

I've included my testing code:

Imports System.Threading
Module Module1
PrivateConst maxGarbageAsInteger = 1000

Sub Main()
Dim AfterAsLong
Dim BeginAsLong
GC.Collect()
Begin = GC.GetTotalMemory(True)
Console.WriteLine("Memory used at start: {0}", Begin)
MakeSomeGarbage()
Console.WriteLine("Memory used before collection: {0}", GC.GetTotalMemory(True))
WhileTrue
GC.Collect()
After = GC.GetTotalMemory(False)
Console.WriteLine("Memory used after full collection: {0}", After)
Console.WriteLine("Difference: {0}", After - Begin)
MakeSomeGarbage()
Console.ReadLine()
EndWhile
EndSub

Sub MakeSomeGarbage()
Dim vtAs LeakingClass
Dim iAsInteger
For i = 0To maxGarbage - 1
vt =New LeakingClass()
Next i
EndSub

Sub CreateGarbage()
Dim lcAs LeakingClass
For iAsInteger = 0To maxGarbage - 1
lc =New LeakingClass()
Next i
EndSub
End Module

Public Class LeakingClass
Public DelegateSub StatusDel()
Public Event StatusAs StatusDel
End Class

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
class Program
{
private const int maxGarbage = 1000;

static void Main()
{
long Begin;
long After;
Begin = GC.GetTotalMemory(true);
Console.WriteLine("Memory used at start: {0}", Begin);
// Put some objects in memory.
MakeSomeGarbage();
Console.WriteLine("Memory used before collection: {0}", GC.GetTotalMemory(false));

// Collect all generations of memory.
while (true)
{
GC.Collect();
After = GC.GetTotalMemory(false);
Console.WriteLine("Memory used after full collection: {0}", GC.GetTotalMemory(true));
Console.WriteLine("Difference: {0}", After - Begin);
MakeSomeGarbage();
Console.ReadLine();
}
}

static void MakeSomeGarbage()
{
LeakingClass vt;

for (int i = 0; i < maxGarbage; i++)
{
// Create objects and release them to fill up memory
// with unused objects.
vt = new LeakingClass();
}
}

}
class LeakingClass
{
public delegate void StatusDel();
public event StatusDel Status;
}

[5232 byte] By [Pidgeon] at [2007-12-24]
# 1
You

are seeing the IDE leaking, it works fine in release mode. I'm

not actually sure that the IDE is really leaking, you can leave it

running for a while and suddenly see chunks of memory being

released. The IDE debugger is known to affect the running

program, especially when it has a lot of New statements.

nobugz at 2007-8-31 > top of Msdn Tech,Visual Basic,Visual Basic Language...
# 2

Take a look at: http://support.microsoft.com/?kbid=919481. The issue is described in the KB article with more details.

Hope this helps,

MSFTAbelValadez at 2007-8-31 > top of Msdn Tech,Visual Basic,Visual Basic Language...