{"id":119201,"date":"2026-07-08T15:56:22","date_gmt":"2026-07-08T10:26:22","guid":{"rendered":"https:\/\/www.guvi.in\/blog\/?p=119201"},"modified":"2026-07-08T15:56:26","modified_gmt":"2026-07-08T10:26:26","slug":"what-is-garbage-collection-in-python","status":"publish","type":"post","link":"https:\/\/www.guvi.in\/blog\/what-is-garbage-collection-in-python\/","title":{"rendered":"What Is Garbage Collection in Python? Explained Simply"},"content":{"rendered":"\n<p>Ever wondered why you rarely have to manually free memory in Python, unlike in C or C++? That&#8217;s garbage collection at work quietly running in the background, cleaning up objects you no longer need. Understanding how it actually works helps you write faster code and catch subtle memory issues before they become real problems.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>TL;DR&nbsp;Summary<\/strong><\/h2>\n\n\n\n<ul>\n<li><strong>Python automatically manages memory through garbage collection<\/strong>, so developers rarely need to manually free unused objects.<\/li>\n\n\n\n<li><strong>Two systems work together:<\/strong> reference counting handles most memory cleanup instantly, while the generational garbage collector detects and removes reference cycles.<\/li>\n\n\n\n<li><strong>Understanding garbage collection helps prevent memory issues<\/strong>, optimize long-running applications, and debug unexpected memory usage.<\/li>\n<\/ul>\n\n\n\n<p><em>Python&#8217;s garbage collection automatically frees memory by removing unused objects. Master Python internals and memory management with HCL GUVI\u2019s <\/em><strong><em>Python Zero to Hero Course. <\/em><\/strong><a href=\"https:\/\/www.guvi.in\/courses\/programming\/python-zero-to-hero\/\" target=\"_blank\" rel=\"noreferrer noopener\"><em>Start your Python journey here<\/em><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What Is Garbage Collection in Python?<\/strong><\/h2>\n\n\n\n<p>Garbage collection is Python&#8217;s automatic system for reclaiming memory from objects that are no longer needed. It works through two mechanisms: reference counting, which tracks how many references point to each object, and a generational garbage collector, which catches reference cycles that reference counting alone can&#8217;t detect.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>How Reference Counting Works<\/strong><\/h2>\n\n\n\n<ol>\n<li><strong>Every Object Tracks Its Own References<\/strong><\/li>\n<\/ol>\n\n\n\n<p>In Python, every object even a simple integer has a reference count attached to it. This count increases whenever a new reference points to the object, and decreases whenever one is removed.<\/p>\n\n\n\n<p><strong>a = [1, 2, 3] b = a<\/strong><\/p>\n\n\n\n<p>Here, both a and b point to the same list. The assignment doesn&#8217;t copy any <a href=\"https:\/\/www.guvi.in\/blog\/guide-to-data-exploration\/\">data <\/a>&nbsp;it just adds a second reference to the same object in memory.<\/p>\n\n\n\n<ol start=\"2\">\n<li><strong>When the Count Hits Zero<\/strong><\/li>\n<\/ol>\n\n\n\n<p>The moment an object&#8217;s reference count drops to zero, CPython immediately deallocates it. This happens through assignment, the del keyword, a variable going out of scope, or a container being deleted.<\/p>\n\n\n\n<p>import sys<\/p>\n\n\n\n<p><strong>x = [1, 2, 3] print(sys.getrefcount(x)) # 2&nbsp; one from x, one from getrefcount itself<\/strong><\/p>\n\n\n\n<p><strong>y = x print(sys.getrefcount(x)) # 3 now x and y both reference it<\/strong><\/p>\n\n\n\n<p><strong>y = None print(sys. getrefcount(x)) # back to 2<\/strong><\/p>\n\n\n\n<p>sys.getrefcount() always reports one more than you&#8217;d expect, because passing the object into the function temporarily creates an extra reference of its own.<\/p>\n\n\n\n<ol start=\"3\">\n<li><strong>Why Reference Counting Is the Default<\/strong><\/li>\n<\/ol>\n\n\n\n<p>Reference counting is fast and predictable; an object is destroyed the instant it&#8217;s no longer needed, not at some unpredictable later point determined by a separate collector thread.&nbsp;<\/p>\n\n\n\n<p>This immediacy is one of CPython&#8217;s defining design choices. It&#8217;s also part of why Python has historically needed the Global Interpreter Lock: updating a reference count safely across multiple threads requires some form of synchronization.<\/p>\n\n\n\n<div style=\"background-color: #099f4e; border: 3px solid #110053; border-radius: 12px; padding: 18px 22px; color: #FFFFFF; font-size: 18px; font-family: Montserrat, Helvetica, sans-serif; line-height: 1.6; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); max-width: 750px;\">\n\n  <strong style=\"font-size: 22px; color: #FFFFFF;\">\ud83d\udca1 Did You Know?<\/strong>\n  <br \/><br \/>\n\n  <strong style=\"color: #FFFFFF;\">CPython<\/strong>, the reference implementation of Python, uses <strong style=\"color: #FFFFFF;\">reference counting<\/strong> to destroy most objects as soon as their reference count reaches zero. However, objects involved in <strong style=\"color: #FFFFFF;\">circular references<\/strong> cannot be reclaimed by reference counting alone. To handle these cases safely, CPython includes a separate <strong style=\"color: #FFFFFF;\">cyclic garbage collector<\/strong> that periodically detects and removes unreachable groups of objects, helping prevent memory leaks in long-running applications.\n\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>The Problem: Reference Cycles<\/strong><\/h2>\n\n\n\n<ol>\n<li><strong>When Objects Point at Each Other<\/strong><\/li>\n<\/ol>\n\n\n\n<p>Reference counting has one fundamental blind spot: it cannot detect reference cycles, where two or more objects reference each other directly or indirectly.<\/p>\n\n\n\n<p><strong>x = [1, 2, 3] y = [4, 5, 6]<\/strong><\/p>\n\n\n\n<p><strong>x.append(y) y.append(x)<\/strong><\/p>\n\n\n\n<p>Now x contains y, and y contains x. Even if nothing else in your program references either list, their reference counts never drop to zero&nbsp; each is still holding the other alive.<\/p>\n\n\n\n<ol start=\"2\">\n<li><strong>Why This Matters<\/strong><\/li>\n<\/ol>\n\n\n\n<p>Without a second mechanism, these objects would simply leak, sitting in memory forever, even though your code can no longer reach them.&nbsp;<\/p>\n\n\n\n<p>Reference cycles aren&#8217;t rare edge cases either; they show up naturally in linked lists, trees with parent pointers, and any object graph where two pieces of data need to know about each other. This is exactly the gap Python&#8217;s generational garbage collector exists to close.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>The Generational Garbage Collector<\/strong><\/h2>\n\n\n\n<ol>\n<li><strong>Three Generations, Sorted by Age<\/strong><\/li>\n<\/ol>\n\n\n\n<p>Python&#8217;s cyclic garbage collector, accessible through the built-in gc module, organises every container object into one of three generations. Generation 0 holds newly created objects. Generation 1 holds objects that survived at least one collection round. Generation 2 holds long-lived objects that have survived multiple rounds.<\/p>\n\n\n\n<p>This generational design is based on a simple observation: most objects die young. By checking generation 0 far more frequently than generation 2, Python avoids repeatedly re-scanning objects that have already proven they&#8217;re long-lived, which keeps collection overhead low.<\/p>\n\n\n\n<ol start=\"2\">\n<li><strong>When Collection Actually Triggers<\/strong><\/li>\n<\/ol>\n\n\n\n<p>Each generation has its own allocation counter and threshold. Every time a new container object is created, Python checks whether generation 0&#8217;s counter has exceeded its threshold. If it has, a collection cycle runs.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import gc print(gc.get_threshold())\n\n(700, 10, 10)&nbsp; exact default can vary slightly by Python version<\/code><\/pre>\n\n\n\n<p>You can inspect and adjust these thresholds directly with gc.set_threshold(), tuning collection frequency for workloads with unusual allocation patterns.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>How the Algorithm Actually Finds Cycles<\/strong><\/h3>\n\n\n\n<ul>\n<li>The cyclic collector works by temporarily excluding references that originate within the tracked set of container objects.&nbsp;<\/li>\n\n\n\n<li>After this pass, any object whose remaining reference count is zero is unreachable from your actual code even though its raw reference count was never literally zero and gets collected.<\/li>\n\n\n\n<li>Only container objects lists, dictionaries, classes, and some tuples&nbsp; are tracked this way.<\/li>\n\n\n\n<li>Simple immutable objects like plain integers and strings are excluded, since they can&#8217;t participate in reference cycles in the first place.<\/li>\n\n\n\n<li>&nbsp;This selective tracking is what keeps the generational collector&#8217;s overhead manageable even in programs that allocate millions of small objects.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Manual Garbage Collection<\/strong><\/h2>\n\n\n\n<ol>\n<li><strong>Forcing a Collection Cycle<\/strong><\/li>\n<\/ol>\n\n\n\n<p>Most of the time, you should let Python&#8217;s GC run on its own schedule. But you can manually trigger a full collection when needed:<\/p>\n\n\n\n<p><strong>import gc<\/strong><\/p>\n\n\n\n<p><strong>a = [1, 2, 3] del a gc.collect()<\/strong><\/p>\n\n\n\n<p>This is occasionally useful right after a section of <a href=\"https:\/\/www.guvi.in\/blog\/how-to-optimize-code-with-generative-ai\/\" target=\"_blank\" rel=\"noreferrer noopener\">code <\/a>that creates a large number of temporary cyclic structures, where you want memory reclaimed immediately rather than waiting for the next automatic trigger.<\/p>\n\n\n\n<ol start=\"2\">\n<li><strong>Disabling the Collector<\/strong><\/li>\n<\/ol>\n\n\n\n<p>You can disable automatic cyclic collection entirely:<\/p>\n\n\n\n<p><strong>import gc gc.disable()<\/strong><\/p>\n\n\n\n<p><strong>&#8230; performance-sensitive section &#8230;<\/strong><\/p>\n\n\n\n<p><strong>gc.enable()<\/strong><\/p>\n\n\n\n<p>This is a real optimization technique in latency-sensitive applications, such as short-lived scripts, for instance, where the cyclic GC&#8217;s periodic pauses add overhead without ever paying off, since the whole process exits before cycles would matter anyway. Reference counting still runs regardless; disabling the GC only stops the cyclic-cycle scanner.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Reference Counting vs Generational GC<\/strong><\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><strong>Aspect<\/strong><\/td><td><strong>Reference Counting<\/strong><\/td><td><strong>Generational GC<\/strong><\/td><\/tr><tr><td>Runs<\/td><td>Immediately, every reference change<\/td><td>Periodically, based on thresholds<\/td><\/tr><tr><td>Detects<\/td><td>Simple &#8220;no references left&#8221; cases<\/td><td>Reference cycles<\/td><\/tr><tr><td>Can be disabled<\/td><td>No&nbsp; fundamental to CPython<\/td><td>Yes&nbsp; via gc.disable()<\/td><\/tr><tr><td>Overhead<\/td><td>Small, constant, per-operation<\/td><td>Occasional, batched pauses<\/td><\/tr><tr><td>Handles<\/td><td>Every object<\/td><td>Only container objects<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><em>Python&#8217;s garbage collection automatically frees memory by removing unused objects. Master Python internals and memory management with HCL GUVI\u2019s <\/em><strong><em>Python Zero to Hero Course. <\/em><\/strong><a href=\"https:\/\/www.guvi.in\/courses\/programming\/python-zero-to-hero\/\" target=\"_blank\" rel=\"noreferrer noopener\"><em>Start your Python journey here<\/em><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Avoiding Reference Cycles in Practice<\/strong><\/h2>\n\n\n\n<ol>\n<li><strong>Use Weak References Where Possible<\/strong><\/li>\n<\/ol>\n\n\n\n<p>The weakref module lets you reference an object without increasing its reference count. If the object is destroyed elsewhere, the weak reference simply returns None instead of keeping it alive artificially.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import weakref\n\nclass Node: pass\n\na = Node() ref = weakref.ref(a) print(ref()) # &lt;Node object&gt;\n\ndel a print(ref()) # None<\/code><\/pre>\n\n\n\n<p>This is the standard fix for cycles that show up in graphs, trees with parent-pointers, or any structure where two objects naturally need to reference each other.<\/p>\n\n\n\n<ol start=\"2\">\n<li><strong>Debugging Cycles With gc.garbage<\/strong><\/li>\n<\/ol>\n\n\n\n<p>Setting gc.set_debug(gc.DEBUG_SAVEALL) tells the collector to save every unreachable cyclic object it finds into the gc. garbage list instead of immediately freeing it, letting you inspect exactly what was being kept alive by a cycle.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>A Note on Python 3.12+: Immortal Objects<\/strong><\/h2>\n\n\n\n<ul>\n<li><a href=\"https:\/\/www.guvi.in\/hub\/python\/\" target=\"_blank\" rel=\"noreferrer noopener\">Python <\/a>3.12 introduced immortal objects, a special reference count value that tells CPython to skip reference counting entirely for certain objects, like None, True, and False, that live for the entire life of the process anyway.<\/li>\n\n\n\n<li>&nbsp;This optimization particularly benefits multiprocessing workloads that rely on copy-on-write memory sharing, since immortal objects never trigger the memory writes that normal reference count changes would otherwise cause.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Conclusion<\/strong><\/h2>\n\n\n\n<p>Python&#8217;s garbage collection is really two systems working together: reference counting handles the vast majority of cleanup instantly and efficiently. In contrast, the generational garbage collector exists specifically to catch the reference cycles that counting alone can never see.&nbsp;<\/p>\n\n\n\n<p>You rarely need to intervene, but understanding both mechanisms helps you reason about memory in long-running applications, recognize when gc.collect() or weakref might genuinely help, and avoid mistaking Python&#8217;s deliberate memory retention for an actual leak.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>FAQs<\/strong><\/h2>\n\n\n<div id=\"rank-math-faq\" class=\"rank-math-block\">\n<div class=\"rank-math-list \">\n<div id=\"faq-question-1783498116340\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>1. What is garbage collection in Python?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Garbage collection is Python&#8217;s automatic memory management system. It identifies objects that are no longer being used and reclaims their memory, helping applications run efficiently without requiring manual memory deallocation.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1783498123143\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>2. How does reference counting work in Python?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Every Python object keeps track of how many references point to it. When the reference count drops to zero, CPython immediately removes the object from memory.<br \/>x = [1, 2, 3]<br \/>y = x<br \/>y = None<br \/>Once no variables reference the object, Python can safely free its memory.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1783498143495\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>3. Why isn&#8217;t reference counting enough on its own?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Reference counting cannot detect circular references. If two objects reference each other, their reference counts never reach zero, even when the rest of the program can no longer access them.<br \/>x = []<br \/>y = []<br \/>x.append(y)<br \/>y.append(x)<br \/>This creates a reference cycle that requires Python&#8217;s garbage collector to clean up.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1783498159811\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>4. What is the generational garbage collector?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Python&#8217;s generational garbage collector is designed to find and remove cyclic references. It organizes tracked objects into generations based on their age and periodically scans them for unreachable cycles.<br \/>The three generations are:<br \/>Generation 0: Newly created objects<br \/>Generation 1: Objects that survived earlier collections<br \/>Generation 2: Long-lived objects<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1783498174592\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>5. Can I manually run garbage collection in Python?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>Yes. The gc module allows you to trigger garbage collection manually.<br \/>import gc<br \/>gc.collect()<br \/>This can be useful after creating large temporary object graphs or when debugging memory-related issues.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1783498207423\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>6. How can I disable Python&#8217;s garbage collector?<\/strong><\/h3>\n<div class=\"rank-math-answer \">\n\n<p>You can disable and re-enable the cyclic garbage collector using the gc module:<br \/>import gc<br \/>gc.disable()<br \/># Performance-sensitive code<br \/>gc.enable()<br \/>Reference counting continues to work even when cyclic garbage collection is disabled.<\/p>\n\n<\/div>\n<\/div>\n<\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Ever wondered why you rarely have to manually free memory in Python, unlike in C or C++? That&#8217;s garbage collection at work quietly running in the background, cleaning up objects you no longer need. Understanding how it actually works helps you write faster code and catch subtle memory issues before they become real problems. TL;DR&nbsp;Summary [&hellip;]<\/p>\n","protected":false},"author":63,"featured_media":119418,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[717],"tags":[],"views":"37","authorinfo":{"name":"Vishalini Devarajan","url":"https:\/\/www.guvi.in\/blog\/author\/vishalini\/"},"thumbnailURL":"https:\/\/www.guvi.in\/blog\/wp-content\/uploads\/2026\/06\/what-is-garbage-collection-in-python-300x116.webp","_links":{"self":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/119201"}],"collection":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/users\/63"}],"replies":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/comments?post=119201"}],"version-history":[{"count":4,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/119201\/revisions"}],"predecessor-version":[{"id":121955,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/119201\/revisions\/121955"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/media\/119418"}],"wp:attachment":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/media?parent=119201"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/categories?post=119201"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/tags?post=119201"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}