Garbage collection is an automated process of Common Language Runtime (CLR) to manage memory by the allocation of memory for live objects and releasing memory for dead objects. Garbage collection can certainly improve performance but not in all cases. Real-time embedded systems can be problematic to automated memory management, the challenge is the successful deployment of safe scheduling of tasks.
Generations
The heap is categorized into generations for reclamations of short-lived objects. The long-lived objects might not be reclaimed because they can cost performance drawback. There are three categories,
- Generation 0
This generation has temporary objects; it contains short-lived objects. Most of the objects from generation 0 do not survive until the next generation because they are reclaimed. It is the youngest generation.
- Generation 1
This generation is a buffer between short-lived objects and long-lived objects. It also contains short-lived objects.
- Generation 2
This generation contains long-lived objects and a reclaim hardly occurs in this generation. The example for such objects is static objects in server applications.
Phases
Garbage Collection has three phases.
- Marking phase
The marking for all live objects is done and a list is created.
- Relocate phase
To relocate the live objects in the next Compact phase, a reference to objects is updated.
- Compact phase
The space is reclaimed from dead objects. The live objects in heap and free memory are compacted in memory to ensure the allocation of new objects in small time. The compaction of large objects is not done because it can create a bad impact on performance.
Triggers for Garbage Collection
The trigger for Garbage collection occurs when,
- The system has low physical memory and gets notification from OS.
- The threshold for an object on heap surpasses; the threshold is not constant but it keeps changing as the process runs.
- GC.Collect is called but it is required in rare cases because Garbage Collection runs continuously and in parallel with processes.
Produce problem and Solution
Some objects like static variables are considered live (roots) and cannot be reclaimed until they refer to any object. Objects referred by local variables inside a method cannot be reclaimed until the local variable referring is reclaimed.
Here is a simple code with two Timers. One is a static object while other is non-static and is inside the Main method.
- static Timer staticTimer;
- static void Main(string[] args)
- {
- Console.Title = "Garbage Collection";
- Timer localTimer = new Timer(LocalOnTimedEvent, null,TimeSpan.Zero,TimeSpan.FromSeconds(1));
-
- staticTimer = new Timer (StaticOnTimedEvent, null,TimeSpan.Zero,TimeSpan.FromSeconds(1));
-
- Console.ReadLine();
-
- }
- private static void LocalOnTimedEvent(Object state)
- {
- Console.WriteLine("Local: {0}", DateTime.Now.TimeOfDay);
-
- GC.Collect();
-
- }
- private static void StaticOnTimedEvent(Object state)
- {
- Console.WriteLine("Shared: {0}", DateTime.Now.TimeOfDay);
-
- GC.Collect();
- }
When we run this code in Debug mode, we get this output.
Both Timers are working as expected. But if I change the configuration and run the code in Release mode, then we come up against one problem.
Please note that the local object is not updating while the static object is still working. Now, to get both timers working, we can add -
- GC.KeepAlive(localTimer);
Now, both timers are working in Release configuration too.
Conclusion
The reason behind different outputs in Debug and Release modes is - localTimer object was reclaimed by Garbage Collector(GC). The Just In Time (JIT) tells GC to keep an object until the end of the method (till "}") to facilitate debugging. In Release mode, GC reclaims objects which are not used after Console.ReadLine() because this line is the end of the method. The static object was still live after the end of methods in both modes.