I have configured Perfmon to monitor the HeapSize of a specific instance of
an application. If I compile that application in debug mode it works as
expected. If I compile in release mode it does not. Is this expected
behavior?
The application is a demonstration of reference vs value vs unmanaged memory
allocation. The application recursively calls one of three methods which
allocates a large string, or a large struct, or a large bitmap. In debug
mode I get the expecte increase in the heap when allocating a large string
recursively, but not when allocating a struct or bitmap. When allocating a
struct I eventually overflow the stack... an expected behavior (occurs at 50
iterations). When allocating the bitmap Total committed memory grows but
not the heap, also an expected behavior.
If I try it with a released version of the same code, the heap never grows,
and I don't get a stack overflow until I get to nearly 10000 iterations.
The commited memory behavor is odd as well... growing and shrinking
dynamically.
Any assistance would be appreciated.
Ben
private string _LargeString;
private int _lastPosition = 0;
private void Form1_Load(object sender, System.EventArgs e)
{
StringBuilder sb = new StringBuilder(10000);
for (int i = 0;i< 10000;i++)
{
sb.Append('a');
}
_LargeString = sb.ToString();
GC.Collect();
GC.Collect();
}
private void btnAllocate10_Click(object sender, System.EventArgs e)
{
for (int i = 0; i < 100;i++)
AllocateOne();
}
int _count = 0;
public void RecursiveAllocateOneRef(Decimal Depth)
{
string x = _LargeString + _count.ToString();
if (Depth == 0)
MessageBox.Show("Allocated " + _count.ToString() + " Copies of Reference.");
else
{
_count++;
RecursiveAllocateOneRef(Depth - 1);
_count--;
}
}
public void RecursiveAllocateOneValue(Decimal Depth)
{
BigStruct s1;
BigStruct s2;
BigStruct s3;
BigStruct s4;
BigStruct s5;
BigStruct s6;
BigStruct s7;
BigStruct s8;
BigStruct s9;
BigStruct s10;
if (Depth == 0)
MessageBox.Show("Allocated " + _count.ToString() + " x 10 Copies of
BigStruct (value) on Stack.");
else
{
_count++;
try
{
RecursiveAllocateOneValue(Depth - 1);
}
catch (StackOverflowException ex)
{
MessageBox.Show(ex.Message,"Count = " + _count.ToString());
}
_count--;
}
}
public void RecursiveAllocateOneUnManaged(Decimal Depth)
{
BigUnmanagedObject umo = new BigUnmanagedObject();
if (Depth == 0)
MessageBox.Show("Allocated " + _count.ToString() + " Copies of
BigUnManagedObject (Ref) on Heap.");
else
{
_count++;
RecursiveAllocateOneUnManaged(Depth - 1);
_count--;
}
}
private void btnRecurseX_Click(object sender, System.EventArgs e)
{
RecursiveAllocateOneRef(Depth.Value);
}
private void btnGCNow_Click(object sender, System.EventArgs e)
{
GC.Collect();
}
private void btnAddXToStack_Click(object sender, System.EventArgs e)
{
RecursiveAllocateOneValue(Depth.Value);
}
private void btnAddXUnmanagedToStack_Click(object sender, System.EventArgs
e)
{
RecursiveAllocateOneUnManaged(Depth.Value);
}
}
public class BigUnmanagedObject
{
Bitmap _bp = new Bitmap(
@"\bigBitmap.bmp");
}
public struct BigStruct
{
public char x1 ;
public char x2 ;
//... a 995of the same
public char x998 ;
public char x999 ;
public char x1000 ;
}
Stephen - 09 Oct 2003 19:38 GMT
Here's my guess, but I'm no expert (more proficient in VB):
When you run it in debug mode, each function has debug
info stored with it and that probably explains your memory
increases, calling them recursively just duplicates this
debug info over and over.
When you run it in release mode, the object you are
creating, such as BigUnmanagedObject umo, are only
declared locally within the function and are destroyed
when the function exits, which is hastened by you calling
GC.Collect() and forcing garbage collection. The ability
for your program to run longer is explained by the fact
there is more room to store the data without all the
recursive debug info.
Stephen