Chris
Thu Feb 15 16:35:11 CST 2007
You've probably got a number of things going on.
1 - You really shouldn't be calling GC.Collect yourself. I know you've heard
this from other people, and probably thought about it a whole lot, but you
really shouldn't be doing it. In several years of building .Net server
applications, I've only come across 1 place where calling GC.Collect has
been appropiate.
2 - 8G of memory doesn't do you any good in .Net. From your post, I'm going
to assume you're running x86 code. This means your code only gets about 1.3
gigs of memory to play with before it hits OOM. You don't get any more than
that, in x86 land. (Does the /3GB switch do anything with the CLR? I don't
think it does...) For more memory, you've got to switch over to x64 or
Itanium.
3 - What CLR are you running? The Server CLR or the Workstation CLR? You'll
get better performance out of the Server CLR, but that means your heap will
be divided up between 4 processors, which means each individual managed heap
will only be 300 megs or so. Before you answer, "I don't know." go look this
up in Google....
4 - Are you doing I/O of some sort? Lots of SQL queries? Network I/O? File
I/O? COM Interpop Calls? I would guess you're doing something. The problem
with this is that you're almost certainly (inadvertantly) pinning chunks of
memory all over the place, thereby creating heap fragmentation. This makes
you run out of memory very quickly, even though it looks like you're only
using 200 or 300 megs of memory.
To debug this, you need to:
1 - Break out a memory profiler and poke around. I like Scitech's memory
profiler (free trial, cheap license, awesome product), but there are a
number of other options.
2 - When your get OOM, use ADPlus to generate a Dump file. Load the Dump
into WinDBG + Son of Strike, and look for heap fragmentation. Details on how
to do this at:
http://www.coversant.net/Default.aspx?tabid=88&EntryID=28
FWIW, GC.Collect can be called from any thread. The places where I call it
for heap compaction prior to Network I/O buffer allocation is all
multi-threaded and works just fine...
--
Chris Mullins, MCSD.NET, MCPD:Enterprise, Microsoft C# MVP
http://www.coversant.com/blogs/cmullins
"don" <don@discussions.microsoft.com> wrote in message
news:57EADA00-71FE-4E1F-9A50-BD43ADE4D64C@microsoft.com...
>
> To take advantage of the hardware power on my company's application
> server,
> which has 4 CPUs and 8Gs of memory, I have created a multi-threaded
> application that would simultaneously process multiple requests to
> generate
> reports.
>
> The main thread of the application would spawn several worker threads to
> process requests and generate reports, and, at the same time, the main
> thread
> would monitor the processing statuses of all worker threads.
>
> To conserve memory, at the end of the class/method, which is associated
> with
> the worker thread, GC.Collect() would be called after calling all the
> heavy
> objects' Dispose methods and setting them to null.
>
> Strangely enough, just one request to generate a series of big reports
> could
> raise OUT OF MEMORY errors.
>
> So I converted the application to be single-threaded and process requests
> one at a time. The application works fine and, at any given time, only 300
> MB
> of memory, at most, are used to process the very same request.
>
> I could not help but conclude that when GC.Collect() is called from a
> worker
> thread, no disposed memory would be re-claimed right away, unless it is
> called from the main thread.
>
> Is my conclusion correct? Or there is something else I do not know about
> GC
> in .NET?
>
> Thanks in advance for any help or advice.
>
> Don Lin
>