views:

282

answers:

5

We have a situation where we are considering forcing a garbage collection on a server that is very low on RAM (3.6/4GB used on average). No, it's not really an option to upgrade this server unfortunately.

One of our service processes (written indirectly in C++ (don't ask...)) does work with the .NET components and then sleeps for 10 minutes. When that service is sleeping, it is often hanging on to 600MB of RAM that could be shared with other processes. Seems somehow related to WSE tracing being turned on for debugging. I can watch it wake up and GC on the next iteration on the first COM call to .NET - however then the process does some work and by the time it goes to sleep the RAM use is back up around 600MB... well, you can see where this goes...

The Question: I'm considering adding a garbage collection just before the process goes to sleep. There are other services on this box that are doing .NET related tasks. When I call a garbage collection in this service process, does that GC affect all other .NET related processes on the box or just the process that requests the collection? I'm a bit worried about creating some sort of performance problem for processes outside of the one I care about.

+8  A: 

It will only affect the process that is calling GC.

jsight
A reference for this would be handy. The documentation I've been able to find says that GC can affect all application domains or just one. I would assume that when you force it, it would only affect the caller, but it'd be nice to see that.
Matt Jordan
AppDomain != process. An AppDomain is a within-process .NET construct that has different security settings, heap, etc. It's almost a mini-process that can be used for loading plugins with different security settings, etc.
Robert Fraser
Good point - I forgot about multiple AppDomains in a process.
Matt Jordan
+1, affecting multiple appdomains is certainly possible, but thats not the way I read the question.
jsight
+4  A: 

Aside from additional CPU utilization for the duration of the collection, forcing a garbage collection does not result in an impact to other .NET processes. However, forcing a garbage collection will not - in most cases - result in a release of physical memory.

The mapping of physical memory to the address space of a process is a complicated thing. A garbage collection in a .NET process may increase the free space in the managed heap - but it isn't guaranteed to result in a decrease in the working set size (the memory your Win32 process is given from the OS). Even if you could get such a scheme working - it would be fragile at best, and may not work under all circumstances.

There is a Win32 method called SetProcessWorkingSetSize() which allows you to control the minimum and maximum physical memory allocated to a process. You may want to look into this in conjunction with the forced collection scheme you are exploring.

LBushkin
+4  A: 

It just affects the process it's called in (but will affect all Application Domains in the process). However, be warned that this will not release the memory to the OS. If you want to release the memory to the OS, a way to do it is:

private static void minimizeMemory()
{
    GC.Collect(GC.MaxGeneration);
    GC.WaitForPendingFinalizers();
    SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle,
        (UIntPtr) 0xFFFFFFFF, (UIntPtr) 0xFFFFFFFF);
}

[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetProcessWorkingSetSize(IntPtr process,
    UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize);

The big caveat here is why. Unless you're noticing that memory is thrashing or swapping is a performance bottleneck (or you're writing an end-user app), there's no real reason to be concerned about memory usage. Forcing a GC can be slow. Reducing the heap will cause the heap to need to be re-allocated from VM. In addition, the .NET GC is reactive, using program behavior to increase its efficiency. By forcing a collection, you don't allow the GC to tune itself to your program's behavior, further reducing performance.

I know this from experience -- I asked this question a while back and even came up with a timer-based class to call the method above after leaving a high-memory operation (run every 5 minutes). Following this, the process working set would reduce itself a lot -- by swapping it to disk. Over the next couple seconds, it would read back the most frequently used parts, a couple minutes later (when the task was run again), it would again do a bunch of allocation. Timing (not even profiling) the process showed that with the collection, there was about a 0.2s (!) slowdown during the allocation of VM from the OS for the task.

Robert Fraser
The why... this service process needs to run every 10 minutes. The box also has an interactive service (sorta like citrix... but different) that allows users to connect. However, when the server runs low on ram, that citrix like service won't allow new connections.
TheToasterThatCould
Ah, I see. Then indeed, you'll want to be process reducing the working set size instead of just doing a GC (which just compacts the heap within the process).
Robert Fraser
+5  A: 

You would be wise to get a profiler out and look at why objects are not being garbage collected efficiently. It may be they're ending up in gen2 unecessarily and you could refactor it to allow it to get rid of objects more quickly.

You could also try ensuring the .NET part runs with the server version of the garbage collector, the default is the workstation version and this prefers to keep the process active rather than running the GC (i.e it's tuned to keep a UI responsive). The server version is more aggressive and runs parallel GCs, one-per core as well.

Add the following to the app.config:

<Configuration>
    <runtime>
        <gcServer enabled=“true“ />
    </runtime>
</Configuration> 

Linky to more information

Paolo
+2  A: 

Along with the other answers referring directly to your use of GC.Collect, I would suggest you not only do some simple profiling, but also do a code review and ensure that you are properly releasing resources when you are done using them.

Do you call Dispose on all objects that implement IDisposable when you are done using them? If you have classes which contain private variables that implement IDisposable, does the containing class implement IDisposable and do you call Dispose?

Are there places where you can use a using statement to ensure Dispose gets called? None of these things will help with physical memory, but it will help with things like HANDLE's to windows objects, etc.

Nick
I'd be a little surprised if the problem was related to this, but I'll check anyway. The COM .NET plug-ins we're calling are running simple functions that make a web service call and return a string (the xml from the ws generally). No global variables, or non-managed objects that I'm aware of.
TheToasterThatCould
COM? Then you'll want to look at whether the COM components are being released properly. Do you need to do any manual calls to Marshal.AddRef and Marshall.ReleaseComObject?
Nick