To understand garbage collection, it’s pertinent to first understand the two areas of memory your program has access to – the stack and heap. At a very high level, the stack is memory that is typically managed for you. That is, when you declare a variable within a function, the memory for that variable is allocated from the stack. When the function returns, or the variable goes out of scope, the memory is automatically freed. Stack memory is allocated for variables whose sizes are known at compile time, and is usually very limited in size—with Visual Studio reportedly only allocating 1MB to stack memory. Stack overflow (not the site we visit hundreds of times per week!) is when the stack memory limit is exceeded. Most often, the operating system will close the offending program when this happens.
Heap, on the other hand, is a much larger pool of memory managed by the operating system and shared among applications and other processes. On modern computers, the heap can be gigabytes in size. The heap is where dynamically allocated memory is requested from. Dynamically allocated memory is typically required when dealing with user or file input, or when you need to allocate a very large object. This makes sense: we don’t know how many records a file will contain, therefore the memory required for these variables cannot be defined at compile time.
The garbage collector works by intermittently “scanning” all object reference trees. Any object that is no longer referenced and not reachable by application code is marked by the garbage collector to be removed, reclaiming the unused memory. This intermittent scanning has the potential to pause your application in order to collect and remove the unused memory. For most applications, this is negligible. There are, however, several areas where this increased performance is important. Many video games are written in languages like C++, where garbage collection kicking in at the wrong time can impact frame rates. Real time systems like finance benefit from the performance increase by being able to instantaneously create and process transactions, where even a slight delay could be problematic or detrimental.
“Modern” languages were created to allow developers the freedom to iterate quickly, without worrying about such low level concepts such as memory management. As we’ve seen though, the topic is still relevant today, and is an interesting one to explore.