Why does calling AppDomain.Unload doesn't result in a garbage collection? - c#

When I perform a AppDomain.Unload(myDomain) I expect it to also do a full garbage collection.
According to Jeffrey Richter in "CLR via C#" he says that during an AppDomain.Unload:
The CLR forces a garbage collection to occur, reclaiming the memory used by any objects
that were created by the now unloaded AppDomain. The Finalize methods for
these objects are called, giving the objects a chance to clean themselves up properly.
According to "Steven Pratschner" in "Customizing .NET Framework Common Language Runtime":
After all finalizers have run and no more threads are executing in the domain, the CLR is ready to unload all the in-memory data structures used in the internal implementation. Before this happens, however, the objects that resided in the domain must be collected. After the next garbage collection occurs, the application domain data structures are unloaded from the process address space and the domain is considered unloaded.
Am I misinterpreting their words?
I did the following solution to reproduce the unexpected behavior (in .net 2.0 sp2):
An class library project called "Interfaces" containing this interface:
public interface IXmlClass
{
void AllocateMemory(int size);
void Collect();
}
A class library project called "ClassLibrary1" which references "Interfaces" and contains this class:
public class XmlClass : MarshalByRefObject, IXmlClass
{
private byte[] b;
public void AllocateMemory(int size)
{
this.b = new byte[size];
}
public void Collect()
{
Console.WriteLine("Call explicit GC.Collect() in " + AppDomain.CurrentDomain.FriendlyName + " Collect() method");
GC.Collect();
Console.WriteLine("Number of collections: Gen0:{0} Gen1:{1} Gen2:{2}", GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));
}
~XmlClass()
{
Console.WriteLine("Finalizing in AppDomain {0}", AppDomain.CurrentDomain.FriendlyName);
}
}
A console application project which references "Interfaces" project and does the following logic:
static void Main(string[] args)
{
AssemblyName an = AssemblyName.GetAssemblyName("ClassLibrary1.dll");
AppDomain appDomain2 = AppDomain.CreateDomain("MyDomain", null, AppDomain.CurrentDomain.SetupInformation);
IXmlClass c1 = (IXmlClass)appDomain2.CreateInstanceAndUnwrap(an.FullName, "ClassLibrary1.XmlClass");
Console.WriteLine("Loaded Domain {0}", appDomain2.FriendlyName);
int tenmb = 1024 * 10000;
c1.AllocateMemory(tenmb);
Console.WriteLine("Number of collections: Gen0:{0} Gen1:{1} Gen2:{2}", GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));
c1.Collect();
Console.WriteLine("Unloaded Domain{0}", appDomain2.FriendlyName);
AppDomain.Unload(appDomain2);
Console.WriteLine("Number of collections after unloading appdomain: Gen0:{0} Gen1:{1} Gen2:{2}", GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));
Console.WriteLine("Perform explicit GC.Collect() in Default Domain");
GC.Collect();
Console.WriteLine("Number of collections: Gen0:{0} Gen1:{1} Gen2:{2}", GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));
Console.ReadKey();
}
The output when running the console application is:
Loaded Domain MyDomain
Number of collections: Gen0:0 Gen1:0 Gen2:0
Call explicit GC.Collect() in MyDomain Collect() method
Number of collections: Gen0:1 Gen1:1 Gen2:1
Unloaded Domain MyDomain
Finalizing in AppDomain MyDomain
Number of collections after unloading appdomain: Gen0:1 Gen1:1 Gen2:1
Perform explicit GC.Collect() in Default Domain
Number of collections: Gen0:2 Gen1:2 Gen2:2
Things to notice:
Garbage collection is done per process (just a refresher)
Objects in the appdomain that gets unloaded have the finalizer called but garbage collection is not done. The 10 megabyte object created by AllocateMemory() will only be collected after performing an explicit GC.Collect() in the above example (or if the garbage collector will at some time later.
Other notes: it doesn't really matter if XmlClass is finalizable or not. The same behavior occurs in the above example.
Questions:
Why does calling AppDomain.Unload doesn't result in a garbage collection? Is there any way to make that call result in a garbage collection?
Inside AllocateMemory() I plan to load short lived large xml documents (less or equal to 16 mb) that will get on LargeObject heap and will be generation 2 objects. Is there any way to have the memory collected without resorting to explicit GC.Collect() or other kind of explicit programmatic control of garbage collector?

Additional Notes:
After some mail exchange with Jeffrey Richter who was kind enough to have a look at the question:
OK, I read your post.
First, the array will not be GC’d until the XMLClass object is GC’d and it takes TWO GCs to collect this object because it contains a Finalize method.
Second, unloading an appdomain at least performs the marking phase of the GC since this is the only way to determine which objects are unreachable so that their Finalize methods can be called.
However, the compact part of the GC might or might not be done when unloading a GC.
Calling GC.CollectionCount obvious does not tell the whole story. It is not showing that the GC marking phase did occur.
And, it’s possible that AppDomain.Unload starts a GC via some internal code which does not cause the collection count variables to be incremented. We already know for a fact that the marking phase is being performed and that collection count is not reflecting this.
A better test would be to look at some object addresses in the debugger and see if compaction actually occurs. If it does (and I suspect it does), then the collection count is just not being updated correctly.
If you want to post this to the web site as my response, you can.
After taking his advice and looking into SOS (also removed the finalizer) it revealed this:
Before AppDomain.Unload:
!EEHeap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0180b1f0
generation 1 starts at 0x017d100c
generation 2 starts at 0x017d1000
ephemeral segment allocation context: none
segment begin allocated size
017d0000 017d1000 01811ff4 0x00040ff4(266228)
Large object heap starts at 0x027d1000
segment begin allocated size
027d0000 027d1000 02f75470 0x007a4470(8012912)
Total Size 0x7e5464(8279140)
------------------------------
GC Heap Size 0x7e5464(8279140)
After AppDomain.Unload (same addresses, no heap compaction was done)
!EEHeap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0180b1f0
generation 1 starts at 0x017d100c
generation 2 starts at 0x017d1000
ephemeral segment allocation context: none
segment begin allocated size
017d0000 017d1000 01811ff4 0x00040ff4(266228)
Large object heap starts at 0x027d1000
segment begin allocated size
027d0000 027d1000 02f75470 0x007a4470(8012912)
Total Size 0x7e5464(8279140)
------------------------------
GC Heap Size 0x7e5464(8279140)
After GC.Collect(), addresses differ indicating heap compaction was done.
!EEHeap -gc
Number of GC Heaps: 1
generation 0 starts at 0x01811234
generation 1 starts at 0x0180b1f0
generation 2 starts at 0x017d1000
ephemeral segment allocation context: none
segment begin allocated size
017d0000 017d1000 01811ff4 0x00040ff4(266228)
Large object heap starts at 0x027d1000
segment begin allocated size
027d0000 027d1000 027d3240 0x00002240(8768)
Total Size 0x43234(274996)
------------------------------
GC Heap Size 0x43234(274996)
After more sos the conclusion I've reached is that it is surely by design, and that heap compaction is not necessarily done. The only thing you can really be sure during an AppDomain unload is that objects will get to be marked as unreachable and will be collected during the next garbage collection (which like I said, it's not done exactly when you unload your application domain, unless there's a coincidence).
EDIT: I've also asked Maoni Stephens, who works directly in the GC team. You can read her response somewhere in the comments here. She confirms that it is by design.
Case closed :)

Probably by design, but I don't understand why you want this behaviour (explicit GC.Collect). As long as the finalizers are called, the objects are removed from the finalizer queue and are ready to be garbage collected if required (the gc thread will kick in when necessary).
You can probably use some nasty unmanaged allocation and some heavy interop, or code it in unmanaged c++ and then use a managed wrapper to access it through C#, but as long as you stay within the managed .Net world, no.
It is more wise to take a second look at your architecture instead of focusing on trying to play the role of the garbage collector.

Related

GC.AddMemoryPressure() not enough to trigger the Finalizer queue execution on time

We have written a custom indexing engine for a multimedia-matching project written in C#.
The indexing engine is written in unmanaged C++ and can hold a significant amount of unmanaged memory in the form of std:: collections and containers.
Every unmanaged index instance is wrapped by a managed object; the lifetime of the unamanaged index is controlled by the lifetime of the managed wrapper.
We have ensured (via custom, tracking C++ allocators) that every byte that is being consumed internally by the indexes is being accounted for, and we update (10 times per second) the managed garbage collector's memory pressure value with the deltas of this value (Positive deltas call GC.AddMemoryPressure(), negative deltas call GC.RemoveMemoryPressure()).
These indexes are thread-safe, and can be shared by a number of C# workers, so there may be multiple references in use for the same index. For that reason, we can not call Dispose() freely, and instead rely on the garbage collector to track reference sharing and eventually to trigger the finalization of the indexes once they are not in use by a worker process.
Now, the problem is that we are running out of memory. Full collections are in fact executed relatively often, however, with the help of a memory profiler, we can find a very large number of "dead" index instances being held in the finalization queue at the point where the process runs out of memory after exhausting the pagination file.
We can actually circumvent the problem if we add a watchdog thread that calls GC::WaitForPendingFinalizers() followed by a GC::Collect() on low memory conditions, however, from what we have read, calling GC::Collect() manually severely disrupts garbage collection efficiency, and we don't want that.
We have even added, to no avail, a pessimistic pressure factor (tried up to 4x) to exaggerate the amount of unmanaged memory reported to the .net side, to see if we could coax the garbage collector to empty the queue faster. It seems as if the thread that processes the queue is completely unaware of the memory pressure.
At this point we feel we need to implement a manual reference counting to Dispose() as soon as the count reaches zero, but this seems to be an overkill, especially because the whole purpose of the memory pressure API is precisely to account for cases like ours.
Some facts:
.Net version is 4.5
App is in 64-bit mode
Garbage collector is running in concurrent server mode.
Size of an index is ~800MB of unmanaged memory
There can be up to 12 "alive" indexes at any point in time.
Server has 64GB of RAM
Any ideas or suggestions are welcome
Well, there will be no answer but "if you want to dispose external resource explicitly you had to do it by yourself".
AddMemoryPressure() method does not guarantee to trigger garbage collection immediately. Instead, CLR uses unmanaged memory allocation/deallocation stats to adjust it's own gc thresholds and GC is triggered only if it is considered appropriate.
Note that RemoveMemoryPressure() does not trigger GC at all (theoretically it can do it due to side effects from actions such as setting GCX_PREEMP but let's skip it for brevity). Instead it decreases the current mempressure value, nothing more (simplifying again).
Actual algorithm is undocumented, however you may look at the implementation from CoreCLR. In short, your bytesAllocated value had to exceed some dynamically calculated limit and then the CLR triggers the GC.
Now the bad news:
In the real app the process is totally unpredictable as each GC collection and each third-party code have an influence on the GC limits. The GC may be called, may be called later on may not be called at all
GC tunes it limits trying to minimize the costly GC2 collections (you're interested in these as you're working with long-lived index objects add they're always promoted to the next generation due to finalizer). So, DDOSing the runtime with huge mem pressure values may strike back as you'll raise the bar high enough to make (almost) no chance to trigger the GC by setting the mem pressure at all.
(NB: the last issue will be fixed with new AddMemoryPressure() implementation but not today, definitely).
UPD: more details.
Ok, lets move on : )
Part 2, or "newer underestimate what _udocumented_ means"
As I've said above, you are interested in GC 2 collections as you are using long-lived objects.
It's well-known fact that the finalizer runs almost immediately after the object was GC-ed (assuming that the finalizer queue is not filled with other objects).
As a proof: just run this gist.
The real reason why your indexes are not freed is pretty obvious: the generation the objects belongs to is not GCed.
And now we're returning to the original question. How do you think, how much memory you had to allocate to trigger the GC2 collection?
As I've said above actual numbers are undocumented. In theory, GC2 may not be called at all until you consume very large chunks of memory.
And now really bad news comes: for server GC "in theory" and "what really happens" are the same.
One more gist, on .Net4.6 x64 the output will be alike this:
GC low latency:
Allocated, MB: 512.19 GC gen 0|1|2, MB: 194.19 | 317.81 | 0.00 GC count 0-1-2: 1-0-0
Allocated, MB: 1,024.38 GC gen 0|1|2, MB: 421.19 | 399.56 | 203.25 GC count 0-1-2: 2-1-0
Allocated, MB: 1,536.56 GC gen 0|1|2, MB: 446.44 | 901.44 | 188.13 GC count 0-1-2: 3-1-0
Allocated, MB: 2,048.75 GC gen 0|1|2, MB: 258.56 | 1,569.75 | 219.69 GC count 0-1-2: 4-1-0
Allocated, MB: 2,560.94 GC gen 0|1|2, MB: 623.00 | 1,657.56 | 279.44 GC count 0-1-2: 4-1-0
Allocated, MB: 3,073.13 GC gen 0|1|2, MB: 563.63 | 2,273.50 | 234.88 GC count 0-1-2: 5-1-0
Allocated, MB: 3,585.31 GC gen 0|1|2, MB: 309.19 | 723.75 | 2,551.06 GC count 0-1-2: 6-2-1
Allocated, MB: 4,097.50 GC gen 0|1|2, MB: 686.69 | 728.00 | 2,681.31 GC count 0-1-2: 6-2-1
Allocated, MB: 4,609.69 GC gen 0|1|2, MB: 593.63 | 1,465.44 | 2,548.94 GC count 0-1-2: 7-2-1
Allocated, MB: 5,121.88 GC gen 0|1|2, MB: 293.19 | 2,229.38 | 2,597.44 GC count 0-1-2: 8-2-1
That's right, in worst cases you had to allocate ~3.5 gig to trigger the GC2 collection. I'm pretty sure that your allocations are much smaller:)
NB: Note that dealing with objects from GC1 generation does not make it any better. The size of GC0 segment may exceed 500mb. You had to try really hard to trigger the garbage collection on the ServerGC :)
Summary: the approach with Add/RemoveMemoryPressure will have (almost) no influence on the garbage collection frequency, at least on server GC.
Now, the last part of the question: what possible solutions do we have?
In short, the simplest possible approach is to do ref-counting via disposable wrappers.
To be continued
we can find a very large number of "dead" index instances being held in the finalization queue
It does not make any sense that these "dead" instances are not getting finalized. After all, you found out that GC::WaitForPendingFinalizers() actually works. So what must be going on here is that they are actually finalized, they are just waiting for the next collection to run so they can get destroyed. And that is taking a while. Yes, that is not unlikely, after all you already called GC::RemoveMemoryPressure() for them. And, hopefully, released the big unmanaged allocation for them.
So this is surely just a false signal, these objects only take up GC heap, not unmanaged heap and GC heap is not your problem.
We have ensured (via custom, tracking C++ allocators) that every byte...
I don't much like the sound of that. Pretty important that the GC calls have some correspondence to actually creating and finalizing managed objects. Very simple to do, you call AddMemoryPressure in your constructor and RemoveMemoryPressure in your finalizer, right after you called the C++ delete operator. The value you pass only needs to be an estimate for the corresponding C++ unmanaged allocation, it doesn't have to be accurate down to the byte, being off by a factor of 2 is not a grave problem. It also doesn't matter that the C++ allocation happens later.
calling GC::Collect() manually severely disrupts garbage collection efficiency
Don't panic. Pretty high odds that, since your unmanaged allocations are so large, that you rarely collect "naturally" and actually need forced allocations. Like the kind that GC::AddMemoryPressure() triggers, it is just as "forced" as calling GC::Collect(). Albeit that it has a heuristic that avoids collecting too frequently, one you might not particularly care about right now :)
Garbage collector is running in concurrent server mode
Don't, use workstation GC, it is much more conservative about heap segment size.
I want to suggest a brief read about "Finalizers are not guaranteed to run". You can test it easily by continuously generating good old Bitmaps by yourself:
private void genButton_Click(object sender, EventArgs e)
{
Task.Run(() => GenerateNewBitmap());
}
private void GenerateNewBitmap()
{
//Changing size also changes collection behavior
//If this is a small bitmap then collection happens
var size = picBox.Size;
Bitmap bmp = new Bitmap(size.Width, size.Height);
//Generate some pixels and Invoke it onto UI if you wish
picBox.Invoke((Action)(() => { picBox.Image = bmp; }));
//Call again for an infinite loop
Task.Run(() => GenerateNewBitmap());
}
It seems on my machine that if I generate more than 500K pixels, I can't generate forever and .NET gives me an OutOfMemoryException.
This thing about Bitmap class was true on 2005 and it is still true in 2015. Bitmap class is important because it exists in the library for a long time. Having bug fixes, performance improvements along the way, what I think is if it can't do something I need, then I need to change my need.
First, the thing about a disposable object is you need to call Dispose by yourself. No, you really need to call it yourself. Seriously. I suggest enabling relevant rules on VisualStudio's code analyze and making use of using etc. appropriately.
Second, calling a Dispose method does not mean calling delete (or free) on the unmanaged side. What I did, and I think you should, is to use reference counting. If your unmanaged side makes use of C++ then I suggest using shared_ptr. Since VS2012, as far as I know, VisualStudio supports shared_ptr.
Therefore, with reference counting, calling a Dispose on your managed object decreases the reference count on your unmanaged object and the unmanaged memory gets deleted only if that reference counts gets down to zero.

Dispose and Finalizer not called, OutOfMemoryException occurs

I am trying to write a class that wraps a buffer allocated with Marshal.AllocHGlobal. I implemented the IDisposable interface, and added a finalizer that should release the memory when I don't need it anymore (when the object goes out of scope).
When I test the class, the GC does not call the finalizer or the Dispose method of my classes, even though they are out of scope. As a result, I get an OutOfMemoryException.
Why does the GC not call the finalizer, and why does the memory not get freed?
Here is a short example that illustrates the problem. In the sample, there is nothing written to the console (except Unhandled Exception: OutOfMemoryException.)
class Buffer : IDisposable
{
public IntPtr buf { get; set; }
public Buffer()
{
buf = Marshal.AllocHGlobal(4 * 1024 * 1024);
}
~Buffer()
{
Console.WriteLine("Finalizer called");
Dispose(false);
}
public void Dispose()
{
Console.WriteLine("Dispose called");
Dispose(true);
GC.SuppressFinalize(this);
}
internal virtual void Dispose(bool disposing)
{
if (buf != IntPtr.Zero)
{
Console.WriteLine("Releasing memory");
Marshal.FreeHGlobal(buf);
buf = IntPtr.Zero;
}
}
}
class Program
{
static void Main(string[] args)
{
while(true)
{
Buffer b = new Buffer();
Thread.Sleep(20);
}
}
}
EDIT: Here is the .NET performance counters for my test program when it crashes:
You need to tell the garbage collector that your very small managed objects with a single IntPtr field have a high cost in terms of unmanaged memory. Currently, the garbage collector is blissfully unaware of the fact that each small managed object uses a large amount of unmanaged memory and has no reason to perform any collection.
You can use the GC.AddMemoryPressure when you allocate the unmanaged memory and GC.RemoveMemoryPressure when you free the unmanaged memory.
Garbage collection occurs when one of the following conditions is true:
The system has low physical memory.
The memory that is used by allocated objects on the managed heap
surpasses an acceptable threshold. This threshold is continuously
adjusted as the process runs.
The GC.Collect method is called. In almost all cases, you do not
have to call this method, because the garbage collector runs
continuously. This method is primarily used for unique situations
and testing.
Also the garbage collector tracks memory only on the managed heap, so for this program, the only condition can trigger GC is the first one.
I compiled the program, if the target CPU is x86, it will through out of memory exception when the private bytes of the process reaches about 2G. When I run the program, I noticed the private bytes increase quickly, but the working set increase very slowly, and also the system physical memory usage increase very slowly.
As private bytes and working set, this post explains:
Private Bytes refer to the amount of memory that the process executable has asked for - not necessarily the amount it is actually using. They are "private" because they (usually) exclude memory-mapped files (i.e. shared DLLs). But - here's the catch - they don't necessarily exclude memory allocated by those files. There is no way to tell whether a change in private bytes was due to the executable itself, or due to a linked library. Private bytes are also not exclusively physical memory; they can be paged to disk or in the standby page list (i.e. no longer in use, but not paged yet either).
Working Set refers to the total physical memory (RAM) used by the process. However, unlike private bytes, this also includes memory-mapped files and various other resources, so it's an even less accurate measurement than the private bytes. This is the same value that gets reported in Task Manager's "Mem Usage" and has been the source of endless amounts of confusion in recent years. Memory in the Working Set is "physical" in the sense that it can be addressed without a page fault; however, the standby page list is also still physically in memory but not reported in the Working Set, and this is why you might see the "Mem Usage" suddenly drop when you minimize an application.
Marshal.AllocHGlobal just increases the private bytes, but the working set is still small, it doesn't trigger GC either.
Please refer this: Fundamentals of Garbage Collection
IDisposable is declarative, The dispose method is only called when Garbage collection actually happens.
Yoy can force garbage collection to happen , for this You need to call
GC.Collect
http://msdn.microsoft.com/en-us/library/xe0c2357(v=vs.110).aspx
I would also recommend using preformance counters to see you app memory consumtion, and see if GC is already called. see here how to do it http://msdn.microsoft.com/en-us/library/x2tyfybc(v=vs.110).aspx

Is correct to use GC.Collect(); GC.WaitForPendingFinalizers();?

I've started to review some code in a project and found something like this:
GC.Collect();
GC.WaitForPendingFinalizers();
Those lines usually appear on methods that are conceived to destruct the object under the rationale of increase efficiency. I've made this remarks:
To call garbage collection explicitly on the destruction of every object decreases performance because doing so does not take into account if it is absolutely necessary for CLR performance.
Calling those instructions in that order causes every object to be destroyed only if other objects are being finalized. Therefore, an object that could be destroyed independently has to wait for another object's destruction without a real necessity.
It can generate a deadlock (see: this question)
Are 1, 2 and 3 true? Can you give some reference supporting your answers?
Although I'm almost sure about my remarks, I need to be clear in my arguments in order to explain to my team why is this a problem. That's the reason I'm asking for confirmation and reference.
The short answer is: take it out. That code will almost never improve performance, or long-term memory use.
All your points are true. (It can generate a deadlock; that does not mean it always will.) Calling GC.Collect() will collect the memory of all GC generations. This does two things.
It collects across all generations every time - instead of what the GC will do by default, which is to only collect a generation when it is full. Typical use will see Gen0 collecting (roughly) ten times as often than Gen1, which in turn collects (roughly) ten times as often as Gen2. This code will collect all generations every time. Gen0 collection is typically sub-100ms; Gen2 can be much longer.
It promotes non-collectable objects to the next generation. That is, every time you force a collection and you still have a reference to some object, that object will be promoted to the subsequent generation. Typically this will happen relatively rarely, but code such as the below will force this far more often:
void SomeMethod()
{
object o1 = new Object();
object o2 = new Object();
o1.ToString();
GC.Collect(); // this forces o2 into Gen1, because it's still referenced
o2.ToString();
}
Without a GC.Collect(), both of these items will be collected at the next opportunity. With the collection as writte, o2 will end up in Gen1 - which means an automated Gen0 collection won't release that memory.
It's also worth noting an even bigger horror: in DEBUG mode, the GC functions differently and won't reclaim any variable that is still in scope (even if it's not used later in the current method). So in DEBUG mode, the code above wouldn't even collect o1 when calling GC.Collect, and so both o1 and o2 will be promoted. This could lead to some very erratic and unexpected memory usage when debugging code. (Articles such as this highlight this behaviour.)
EDIT: Having just tested this behaviour, some real irony: if you have a method something like this:
void CleanUp(Thing someObject)
{
someObject.TidyUp();
someObject = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
... then it will explicitly NOT release the memory of someObject, even in RELEASE mode: it'll promote it into the next GC generation.
There is a point one can make that is very easy to understand: Having GC run automatically cleans up many objects per run (say, 10000). Calling it after every destruction cleans up about one object per run.
Because GC has high overhead (needs to stop and start threads, needs to scan all objects alive) batching calls is highly preferable.
Also, what good could come out of cleaning up after every object? How could this be more efficient than batching?
Your point number 3 is technically correct, but can only happen if someone locks during a finaliser.
Even without this sort of call, locking inside a finaliser is even worse than what you have here.
There are a handful of times when calling GC.Collect() really does help performance.
So far I've done so 2, maybe 3 times in my career. (Or maybe about 5 or 6 times if you include those where I did it, measured the results, and then took it out again - and this is something you should always measure after doing).
In cases where you're churning through hundreds or thousands of megs of memory in a short period of time, and then switching over to much less intensive use of memory for a long period of time, it can be a massive or even vital improvement to explicitly collect. Is that what's happening here?
Anywhere else, they're at best going to make it slower and use more memory.
See my other answer here:
To GC.Collect or not?
two things can happen when you call GC.Collect() yourself: you end up spending more time doing collections (because the normal background collections will still happen in addition to your manual GC.Collect()) and you'll hang on to the memory longer (because you forced some things into a higher order generation that didn't need to go there). In other words, using GC.Collect() yourself is almost always a bad idea.
About the only time you ever want to call GC.Collect() yourself is when you have specific information about your program that is hard for the Garbage Collector to know. The canonical example is a long-running program with distinct busy and light load cycles. You may want to force a collection near the end of a period of light load, ahead of a busy cycle, to make sure resources are as free as possible for the busy cycle. But even here, you might find you do better by re-thinking how your app is built (ie, would a scheduled task work better?).
We have run into similar problems to #Grzenio however we are working with much larger 2-dimensional arrays, in the order of 1000x1000 to 3000x3000, this is in a webservice.
Adding more memory isn't always the right answer, you have to understand your code and the use case. Without GC collecting we require 16-32gb of memory (depending on customer size). Without it we would require 32-64gb of memory and even then there are no guarantees the system won't suffer. The .NET garbage collector is not perfect.
Our webservice has an in-memory cache in the order of 5-50 million string (~80-140 characters per key/value pair depending on configuration), in addition with each client request we would construct 2 matrices one of double, one of boolean which were then passed to another service to do the work. For a 1000x1000 "matrix" (2-dimensional array) this is ~25mb, per request. The boolean would say which elements we need (based on our cache). Each cache entry represents one "cell" in the "matrix".
The cache performance dramatically degrades when the server has > 80% memory utilization due to paging.
What we found is that unless we explicitly GC the .net garbage collector would never 'cleanup' the transitory variables until we were in the 90-95% range by which point the cache performance had drastically degraded.
Since the down-stream process often took a long duration (3-900 seconds) the performance hit of a GC collection was neglible (3-10 seconds per collect). We initiated this collect after we had already returned the response to the client.
Ultimately we made the GC parameters configurable, also with .net 4.6 there are further options. Here is the .net 4.5 code we used.
if (sinceLastGC.Minutes > Service.g_GCMinutes)
{
Service.g_LastGCTime = DateTime.Now;
var sw = Stopwatch.StartNew();
long memBefore = System.GC.GetTotalMemory(false);
context.Response.Flush();
context.ApplicationInstance.CompleteRequest();
System.GC.Collect( Service.g_GCGeneration, Service.g_GCForced ? System.GCCollectionMode.Forced : System.GCCollectionMode.Optimized);
System.GC.WaitForPendingFinalizers();
long memAfter = System.GC.GetTotalMemory(true);
var elapsed = sw.ElapsedMilliseconds;
Log.Info(string.Format("GC starts with {0} bytes, ends with {1} bytes, GC time {2} (ms)", memBefore, memAfter, elapsed));
}
After rewriting for use with .net 4.6 we split the garbage colleciton into 2 steps - a simple collect and a compacting collect.
public static RunGC(GCParameters param = null)
{
lock (GCLock)
{
var theParams = param ?? GCParams;
var sw = Stopwatch.StartNew();
var timestamp = DateTime.Now;
long memBefore = GC.GetTotalMemory(false);
GC.Collect(theParams.Generation, theParams.Mode, theParams.Blocking, theParams.Compacting);
GC.WaitForPendingFinalizers();
//GC.Collect(); // may need to collect dead objects created by the finalizers
var elapsed = sw.ElapsedMilliseconds;
long memAfter = GC.GetTotalMemory(true);
Log.Info($"GC starts with {memBefore} bytes, ends with {memAfter} bytes, GC time {elapsed} (ms)");
}
}
// https://msdn.microsoft.com/en-us/library/system.runtime.gcsettings.largeobjectheapcompactionmode.aspx
public static RunCompactingGC()
{
lock (CompactingGCLock)
{
var sw = Stopwatch.StartNew();
var timestamp = DateTime.Now;
long memBefore = GC.GetTotalMemory(false);
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();
var elapsed = sw.ElapsedMilliseconds;
long memAfter = GC.GetTotalMemory(true);
Log.Info($"Compacting GC starts with {memBefore} bytes, ends with {memAfter} bytes, GC time {elapsed} (ms)");
}
}
Hope this helps someone else as we spent a lot of time researching this.
[Edit] Following up on this, we have found some additional problems with the large matrices. we have started encountering heavy memory pressure and the application suddenly being unable to allocate the arrays, even if the process/server has plenty of memory (24gb free). Upon deeper investigation we discovered that the process had standby memory that was almost 100% of the "in use memory" (24gb in use, 24gb standby, 1gb free). When the "free" memory hit 0 the application would pause for 10+ seconds while standby was reallocated as free and then it could start responding to requests.
Based on our research this appears to be due to fragmentation of the large object heap.
To address this concern we are taking 2 approaches:
We are going to change to jagged array vs multi-dimensional arrays. This will reduce the amount of continuous memory required, and ideally keep more of these arrays out of the Large Object Heap.
We are going to implement the arrays using the ArrayPool class.
I've used this just once: to clean up server-side cache of Crystal Report documents. See my response in Crystal Reports Exception: The maximum report processing jobs limit configured by your system administrator has been reached
The WaitForPendingFinalizers was particularly helpful for me, as sometimes the objects were not being cleaned up properly. Considering the relatively slow performance of the report in a web page - any minor GC delay was negligible, and the improvement in memory management gave an overall happier server for me.

Garbage collection and references C#

I have a design and I am not sure if garbage collection will occur correctly.
I have some magic apples, and some are yummy some are bad.
I have a dictionary : BasketList = Dictionary <basketID,Basket>
(list of baskets).
Each Basket object has a single Apple in it and each Basket stores a reference to an objectAppleSeperation.AppleSeperation stores 2 dictionaries, YummyApples = <basketID,Apple> and BadApples = Dictionary<basketID,Apple>, so when I'm asked where an apple is I know.
An Apple object stores BasketsImIn = Dictionary<ID,Basket>, which points to the Basket and in Shops, and the Apple in Basket.
My question is, if I delete a basket from BasketList and make sure I delete the Apple from BadApples and/or YummyApples, will garbage collection happen properly, or will there be some messy references lying around?
You are right to be thinking carefully about this; having the various references has implications not just for garbage collection, but for your application's proper functioning.
However, as long as you are careful to remove all references you have set, and you don't have any other free-standing variables holding onto the reference, the garbage collector will do its work.
The GC is actually fairly sophisticated at collecting unreferenced objects. For example, it can collect two objects that reference each other, but have no other 'living' references in the application.
See http://msdn.microsoft.com/en-us/library/ee787088.aspx for fundamentals on garbage collection.
From the above link, when garbage collection happens...
The system has low physical memory.
The memory that is used by allocated objects on the managed heap surpasses an acceptable threshold. This means that a threshold of acceptable memory usage has been exceeded on the managed heap. This threshold is continuously adjusted as the process runs.
The GC.Collect method is called. In almost all cases, you do not have to call this method, because the garbage collector runs continuously. This method is primarily used for unique situations and testing.
If you are performing the clean-up properly, then you need not worry!

How does a new statement allocate heap memory?

private button btnNew=new button();
btnNew.addclickhandler(this);
private DataGrid grid;
private void onClick(event click) {grid=new DataGrid();}
Hello ,I write a code like this sample ,I want to know that every time a user click on btnNew,what is going on in heap and stack memory?for example does a new block in heap memory assign to this grid?Or an older block remove and this new block replace it ?Or an older block remains in heap memory and also new block assign to it.
Is this block of code allocate a huge memory on several click?
**The DataGrid could be replace with any component I want to know about this type of new statement usage and memory allocation **
sorry, for my bad english!
.
what is going on with respect to heap
and stack memory?
since the button is reference type and declared in global will be allocated in heap, not in stack.
Is a new block in heap memory assigned to this button?
yes if memory is available, else unreached references will be removed and this one is allocated
Does this block of code allocate a
large amount of memory on a single
click?
No, but it will, if you add thousand buttons
Check out this cool article Memory in .NET - what goes where by Jon Skeet to understand the memory internals better..
Cheers
This is a huge topic. This is akin to asking "you type www.amazon.com into a browser. What happens next?" To answer that question fully you have to explain the architecture of the entire internet. To answer your question fully you have to understand the entire memory model of a modern operating system.
You should start by reading about the fundamentals of memory and garbage collection, here:
http://msdn.microsoft.com/en-us/library/ee787088.aspx
and then ask more specific questions about things you don't understand.
Using the new statement allocates memory on the heap. In general new memory is allocated. If the btnNew pointer was the only pointer associated with a button object, it should become a target to the garbage collector. So the memory will be freed again. For multiple clicks the same will happen, but you should be aware that the garbage collector does not work in real time. So in a high-frequency loop allocating large objects - "new" can become a problem in c#.
if button is a class (ref type), it is allocated on the heap. (value-types are allocated on the stack in the current CLR implementation, unless they are contained by another reference type or captured in a closure - in which case they are on the heap.).
The garbage collector has pre-allocated segments of memory of different sizes corresponding to generations 0, 1 and 2. WHen you new up an object, it is allocated in generation 0. And this allocation is really fast, since it is just moving a pointer by a delta = size of the object. The CLR clears the values in the object to default values as a prereq step before executing the ctor.
Periodically all threads are paused and the garbage collector runs. It creates a graph of reachable objects by traversing "roots". All unreachable objects are discarded. The generation segments are moved around / compacted to avoid fragmentation. Gen 0 is collected more frequently than 1 and so on... (since Gen-0 objects are likely to be short-lived objects). After the collection, the app threads resume.
For more on this, refer to documents explaining the garbage collector and generations. Here's one.

Categories

Resources