Garbage collection is a feature of many languages, such as C # and Java. While manual memory management, such as C ++, can be quite fast, automatic garbage collection improves quality of life for developers. However, it is important to understand the performance implications of leaving GC to do your job.
Stack versus hope
To understand what a garbage collector does, you must first understand the difference between the memory stored on the stack and the memory stored on the heap. Both are specific memory locations in the memory allocated to your program, from your computer̵
The Stack is fast and is used for fixed-byte value types. It’s the same physical memory as the heap, of course, it’s just fast because it’s a highly ordered First-In, Last-Out data structure. Whenever you create a local variable it will store it in the stack, and when your function exits and the variable is out of scope, it will be automatically removed from the stack.
This is a very fast process and basically makes stack allocations free. While there is a performance penalty, it’s as cheap as it can get.
The Hope, on the other hand, is used for large objects such as lists and strings that are too large to be stored on the stack, or that need to be stored long after functions are out of scope, which is not by design for stack assignments. anytime you want
new object, you make a lot of allocation. You probably also create a stack map, because if you store a reference in a local variable, that local variable must be created to reference the actual memory of the new object.
The heap is a lot slower, both for allocating and removing memory. This applies to all languages using this model, with or without garbage collector.
Clean up your waste
Of course, it is not as simple as ‘assign it once and forget it’. If we never removed the memory, we would have a memory leak, that is very bad and will quickly eat up your machine’s RAM. Assignments such as local lists will immediately fall out of scope, but without a cleanup, hope would be clogged forever. So programs must have a way to clean up memory that is no longer needed.
In manual memory management languages, such as C ++, memory is handled manually. You must manually free up memory and delete objects that you no longer use using a reference or pointer to that object’s memory location. While that can be extremely fast, coding isn’t fun and can lead to memory bugs and exploits. This is one of the main reasons why C ++ is seen as a “hard” programming language to learn and code in.
The alternative to manual management is to let the machine do it automatically for you. This is what is known as garbage collection.
A garbage collector runs on a background thread and periodically scans the heap and stack of your application, looking for objects that no longer have references. This means that the object is worthless and can be safely removed without affecting the program.
Take for example the following pseudocode, which creates and “removes” an object
object refToObject = new object(); refToObject = null;
refToObject no longer refers to it
new object() that is created, the garbage collector will see the new object dangling with no reference to it anywhere, and delete it the next time it collects garbage.
The garbage collector is also quite smart and can solve circular dependencies. For example, if you have two objects that refer to each other, but you don’t know anything else, it’s mess. In most cases, if an object doesn’t have a chain of reference that starts at the root of the program and leads to the object, it’s garbage.
Garbage collection can be activated at any time, usually:
- When the system is low on memory.
- The percentage of memory on the heap exceeds a certain threshold. This threshold is automatically adjusted and is basically whenever the GC sees that your program needs cleaning.
- When triggered manually, such as with
Effects on performance
Of course, waste collection is not free at all. If it were, any language would use it. GC is slow, mainly because it has to pause program execution to collect junk.
Think of it like this: your CPU can only work on one thing at a time. With C ++, it always works on your code, including the bits that are removing memory. With a GC, your program won’t delete memory and it will run until it makes some mess. Then it pauses and the CPU switches to working on garbage collection. Doing this often can slow down the application’s performance.
However, it is usually quite fast, usually no more than a few milliseconds. For .NET, this depends on the kind of memory being cleaned, as it keeps track of memory in several “generations”:
- Generation 0, the youngest generation that includes ephemeral objects such as temporal variables.
- Generation 1, which acts as a buffer between short and long term objects. If an object survives a garbage collection attempt, it is “promoted” to a higher generation.
- Generation 2, the latter, which follows long-term objects.
The GC checks objects in Gen0, then Gen1, then Gen2. Since they only contain temporary or newly created objects, cleaning Gen0 and Gen1 is usually quite fast, but Gen2 contains a lot of memory. Performing a “full garbage collection” can be a lot slower than a temporary garbage collection.
How to speed up performance?
So what can you do to avoid this? Your waste must be collected at the end of the day. All you can really do is reduce the amount of waste your program throws around.
One of the most important ways to reduce waste is to use Pool objects. The core principle behind this is that it is often faster to reset objects to default settings than to create a new one and throw the old one away.
For example, the following code repeats 10k times and makes a new list each time to do something with it. However, this is awful on the GC, so a better method is to create one big list and delete it after you are done with it and want a new one.
In practice, this is usually done with a generic “Object Pool”, which manages a list of objects that it can “rent” to your program. When your code is ready, it frees the object back to the pool and resets it, ready to be used again when prompted.