[00:00] Garbage collection makes programming in modern languages a lot easier. At its core, it's about reclaiming memory that's no longer in use by a program. But why does it matter? Without effective memory management, programs can gradually eat up more and more memory, [00:15] leading to slower performance, crashes, and outright failure. Today, we're diving into what garbage collection is and how it works in popular languages. At its heart, garbage collection revolves around a simple question. [00:27] Which objects in memory do the program still use? This question is answered through the concept of reachability. Every program has GC roots. These are starting points like global variables and stack references. [00:40] Any object that can be reached by following references from these roots is considered alive and must be kept. Everything else is garbage, ready to be collected. To efficiently manage memory, garbage collectors typically implement a generational hierarchy. [00:54] This design is based on an empirical observation. most objects die young. In Java Virtual Machine, memory is divided into three main areas, the young generation, old generation, and metaspace. New objects start life in the [01:09] young generation Eden space If they survive multiple collection cycles they graduate to the survivor space within the young generation The rare objects that persist even longer earn promotion to the old generation where collection happens less frequently but more thoroughly [01:26] The metaspace is Java-specific, used for class metadata to help reduce memory footprint in large applications. Other languages might implement generational collection differently. For instance, V8 uses a two-generation system, [01:41] and .NET's garbage collector typically uses three generations, numbers 0, 1, and 2. The most fundamental garbage collector strategy is the mark-and-sweep algorithm. It works in two phases. [01:54] First, during the mark phase, it traverses all references starting from the GC roots, marking each reachable object. Then, in the sweep phase, it reclaims memory from any unmarked objects. [02:06] While effective, this basic approach requires the application to completely pause during collection, known as a stop-the-world pause, which can freeze the applications for noticeable periods of time. [02:19] These pauses become more problematic as heap sizes grow and applications demand better responsiveness. An enhanced version called the tricolor mark algorithm reduces these pauses by categorizing objects into three sets White objects are considered potential garbage Gray objects are known to be reachable but haven been fully explored [02:41] Black objects are both reachable and fully processed. By maintaining these three distinct sets, the garbage collector can pause briefly to do initial marking, then continue examining gray objects and their references while the application runs. [02:56] This incremental approach avoids the long pause required required by traditional mock and sweep, where the entire object graph must be traced at once. Languages take different approaches to garbage collection. Java, for instance, [03:09] offers several GC algorithms, serial, parallel, CMS, and G1. The evolution of these algorithms reflects the need to balance performance, latency, and scalability across different [03:22] application types. They are designed to handle everything from small-scale apps to massive enterprise systems, often using a generational model to optimize performance. Python uses a combination of reference counting and a cyclic garbage collector. [03:37] The reference counting handles most cases by automatically deallocating objects when their reference count drops to zero. The cyclic collector cleans up circular references which reference counting alone can manage Go uses a concurrent mark and sweep collector which operates alongside the application to minimize pause times [03:58] It leverages a tricolor marking algorithm, mentioned before, to handle reachability efficiently. This allows garbage collection to proceed incrementally without significantly disrupting application performance. [04:11] As helpful as garbage collection is, it's not without drawbacks. For one, there's the performance overhead. GC cycles can introduce unpredictable pauses, which might not matter for some applications, but could be a problem for latency-sensitive [04:25] systems. There's also memory fragmentation. Some collectors leave gaps in memory, making allocations slower over time. Memory management also involves balancing used pools and free [04:37] pools to ensure efficient allocation and deallocation without fragmentation. With garbage collection, we usually lose fine-grained control over when cleanup happens. This can lead to unpredictable pauses in your application. [04:52] If you like our video, you might like our system design newsletter as well. It covers topics and trends in large-scale system design, trusted by 1 million readers.