M
MAG1301
I've detected memory leaks in our huge .NET 1.1 C# application but
couldn't localize them directly. So I've reduced the code to the
following console application:
using System;
using System.IO;
namespace MemLeak
{
class MemLeak
{
[STAThread]
static void Main(string[] args)
{
try
{
throw new NullReferenceException();
}
catch
{
}
for(int i=0; i<1000; i++)
{
using(StreamWriter newMasterfile =
new StreamWriter(@"c:\XXX.tmp"))
{
newMasterfile.Close();
}
}
Console.ReadLine();
}
}
}
Also you have to create an "App.config" file which is distributed by
VS2003 into the application directory with the name of the application
followed by ".config". There may be any content, the file can be empty
too - this will not influence our result.
Compile the "Release" configuration and you'll see with a tool like
"NetMemoryProfiler" that there are almost 1000 StreamWriter reserved at
"Console.ReadLine". You can wait as long as you want - they'll never be
freed. If you don't have such a tool: increase the number of loops.
Then you can use the task manager to see how the memory grows (look at
the virtual memory!). With an endless loop there will be an "Out of
virtual memory" error at the end.
This problem is not only bounded to StreamWriter. We've seen this for
database objects, simple strings and other.
We have tested this with several Windows XP SP2 Systems with different
configurations. Also with the latest fixes for Windows and .NET.
The funny thing: there will be no leaks when you use the "Debug"
configuration. Also you can omit the exception or the "App.config"
file. In these cases the memory will be freed correctly. But we will
comile in "Release" configuration, use the "App.config" and Exceptions
can't be avoided.
The last option is to switch the attribute "[STAThread]" to
"[MTAThread]". The memory is ok in the small example (why?) and also in
our huge application. But I'm not sure with the COM-components we use.
So I prefer the "[STAThread]" attribute.
There are some methods which you can tell the gabage collector to clean
up: "GC.GetTotalMemory(...)", "GC.WaitForPendingFinalizers()" and
"GC.Collect()". You can find combinations in the small example to get
rid of the memory. But this is very unreliable and will not work
correctly in our huge application where late binding and remoting is
used.
After some days of trial and error, I am really at my wits' end over
this problem. So is there anybody who can help me?
Many thanks in advance
Martin Gaus
couldn't localize them directly. So I've reduced the code to the
following console application:
using System;
using System.IO;
namespace MemLeak
{
class MemLeak
{
[STAThread]
static void Main(string[] args)
{
try
{
throw new NullReferenceException();
}
catch
{
}
for(int i=0; i<1000; i++)
{
using(StreamWriter newMasterfile =
new StreamWriter(@"c:\XXX.tmp"))
{
newMasterfile.Close();
}
}
Console.ReadLine();
}
}
}
Also you have to create an "App.config" file which is distributed by
VS2003 into the application directory with the name of the application
followed by ".config". There may be any content, the file can be empty
too - this will not influence our result.
Compile the "Release" configuration and you'll see with a tool like
"NetMemoryProfiler" that there are almost 1000 StreamWriter reserved at
"Console.ReadLine". You can wait as long as you want - they'll never be
freed. If you don't have such a tool: increase the number of loops.
Then you can use the task manager to see how the memory grows (look at
the virtual memory!). With an endless loop there will be an "Out of
virtual memory" error at the end.
This problem is not only bounded to StreamWriter. We've seen this for
database objects, simple strings and other.
We have tested this with several Windows XP SP2 Systems with different
configurations. Also with the latest fixes for Windows and .NET.
The funny thing: there will be no leaks when you use the "Debug"
configuration. Also you can omit the exception or the "App.config"
file. In these cases the memory will be freed correctly. But we will
comile in "Release" configuration, use the "App.config" and Exceptions
can't be avoided.
The last option is to switch the attribute "[STAThread]" to
"[MTAThread]". The memory is ok in the small example (why?) and also in
our huge application. But I'm not sure with the COM-components we use.
So I prefer the "[STAThread]" attribute.
There are some methods which you can tell the gabage collector to clean
up: "GC.GetTotalMemory(...)", "GC.WaitForPendingFinalizers()" and
"GC.Collect()". You can find combinations in the small example to get
rid of the memory. But this is very unreliable and will not work
correctly in our huge application where late binding and remoting is
used.
After some days of trial and error, I am really at my wits' end over
this problem. So is there anybody who can help me?
Many thanks in advance
Martin Gaus