[{"data":1,"prerenderedAt":486},["ShallowReactive",2],{"blog-\u002Fblog\u002Fpython-garbage-collection-explained":3},{"id":4,"title":5,"body":6,"description":471,"difficulty":472,"extension":473,"framework":474,"frameworkSlug":48,"meta":475,"navigation":476,"order":57,"path":477,"qaPath":478,"seo":479,"stem":480,"subtopic":481,"topic":482,"topicSlug":483,"updated":484,"__hash__":485},"blog\u002Fblog\u002Fpython-garbage-collection-explained.md","Python Garbage Collection Explained — Reference Counting, Cycle Detection, and the gc Module",{"type":7,"value":8,"toc":460},"minimark",[9,14,32,36,43,124,127,131,138,210,213,217,236,264,267,271,278,305,308,314,331,403,406,410,424,428,456],[10,11,13],"h2",{"id":12},"python-garbage-collection-explained","Python garbage collection, explained",[15,16,17,18,22,23,27,28,31],"p",{},"CPython frees memory automatically, but ",[19,20,21],"em",{},"how"," matters in interviews and in debugging leaks.\nThere are two mechanisms working together: ",[24,25,26],"strong",{},"reference counting"," does almost all the work\ninstantly, and a ",[24,29,30],{},"cyclic garbage collector"," cleans up the one case reference counting\ncan't — reference cycles. Understanding both explains a lot of Python's behaviour.",[10,33,35],{"id":34},"reference-counting-is-the-primary-mechanism","Reference counting is the primary mechanism",[15,37,38,39,42],{},"Every object carries a count of how many references point to it. When the count hits zero,\nthe object is freed ",[24,40,41],{},"immediately"," — deterministically, the moment its last reference goes\naway.",[44,45,50],"pre",{"className":46,"code":47,"language":48,"meta":49,"style":49},"language-python shiki shiki-themes github-light github-dark","import sys\nx = []\nsys.getrefcount(x)      # 2 — x, plus the temporary arg to getrefcount itself\ny = x\nsys.getrefcount(x)      # 3 — now y references it too\ndel y\nsys.getrefcount(x)      # 2 again\n","python","",[51,52,53,66,78,88,99,107,116],"code",{"__ignoreMap":49},[54,55,58,62],"span",{"class":56,"line":57},"line",1,[54,59,61],{"class":60},"szBVR","import",[54,63,65],{"class":64},"sVt8B"," sys\n",[54,67,69,72,75],{"class":56,"line":68},2,[54,70,71],{"class":64},"x ",[54,73,74],{"class":60},"=",[54,76,77],{"class":64}," []\n",[54,79,81,84],{"class":56,"line":80},3,[54,82,83],{"class":64},"sys.getrefcount(x)      ",[54,85,87],{"class":86},"sJ8bj","# 2 — x, plus the temporary arg to getrefcount itself\n",[54,89,91,94,96],{"class":56,"line":90},4,[54,92,93],{"class":64},"y ",[54,95,74],{"class":60},[54,97,98],{"class":64}," x\n",[54,100,102,104],{"class":56,"line":101},5,[54,103,83],{"class":64},[54,105,106],{"class":86},"# 3 — now y references it too\n",[54,108,110,113],{"class":56,"line":109},6,[54,111,112],{"class":60},"del",[54,114,115],{"class":64}," y\n",[54,117,119,121],{"class":56,"line":118},7,[54,120,83],{"class":64},[54,122,123],{"class":86},"# 2 again\n",[15,125,126],{},"This is why CPython releases resources promptly: when a local goes out of scope, its\nreferents' counts drop, and unreferenced objects die right away — no waiting for a collector.",[10,128,130],{"id":129},"why-reference-counting-isnt-enough","Why reference counting isn't enough",[15,132,133,134,137],{},"Reference counting can't free ",[24,135,136],{},"cycles"," — objects that reference each other. Their counts\nnever reach zero even when nothing outside the cycle points at them.",[44,139,141],{"className":46,"code":140,"language":48,"meta":49,"style":49},"a = {}\nb = {}\na[\"b\"] = b      # a references b\nb[\"a\"] = a      # b references a — a cycle\ndel a, b        # outside refs gone, but each still has refcount 1 → leaked\n",[51,142,143,153,162,182,200],{"__ignoreMap":49},[54,144,145,148,150],{"class":56,"line":57},[54,146,147],{"class":64},"a ",[54,149,74],{"class":60},[54,151,152],{"class":64}," {}\n",[54,154,155,158,160],{"class":56,"line":68},[54,156,157],{"class":64},"b ",[54,159,74],{"class":60},[54,161,152],{"class":64},[54,163,164,167,171,174,176,179],{"class":56,"line":80},[54,165,166],{"class":64},"a[",[54,168,170],{"class":169},"sZZnC","\"b\"",[54,172,173],{"class":64},"] ",[54,175,74],{"class":60},[54,177,178],{"class":64}," b      ",[54,180,181],{"class":86},"# a references b\n",[54,183,184,187,190,192,194,197],{"class":56,"line":90},[54,185,186],{"class":64},"b[",[54,188,189],{"class":169},"\"a\"",[54,191,173],{"class":64},[54,193,74],{"class":60},[54,195,196],{"class":64}," a      ",[54,198,199],{"class":86},"# b references a — a cycle\n",[54,201,202,204,207],{"class":56,"line":101},[54,203,112],{"class":60},[54,205,206],{"class":64}," a, b        ",[54,208,209],{"class":86},"# outside refs gone, but each still has refcount 1 → leaked\n",[15,211,212],{},"Without a second mechanism, this memory would be lost forever. That's the cyclic collector's\njob.",[10,214,216],{"id":215},"the-cyclic-garbage-collector","The cyclic garbage collector",[15,218,219,220,223,224,227,228,231,232,235],{},"The ",[51,221,222],{},"gc"," module runs a separate ",[24,225,226],{},"cycle detector",". It periodically finds groups of objects\nthat are only reachable from each other and collects them. It only tracks ",[24,229,230],{},"container","\nobjects (lists, dicts, classes, etc.) — things that ",[19,233,234],{},"can"," form cycles; simple immutables\nlike ints and strings are never tracked.",[44,237,239],{"className":46,"code":238,"language":48,"meta":49,"style":49},"import gc\ngc.collect()        # force a full collection now, returns objects collected\ngc.isenabled()      # True by default\n",[51,240,241,248,256],{"__ignoreMap":49},[54,242,243,245],{"class":56,"line":57},[54,244,61],{"class":60},[54,246,247],{"class":64}," gc\n",[54,249,250,253],{"class":56,"line":68},[54,251,252],{"class":64},"gc.collect()        ",[54,254,255],{"class":86},"# force a full collection now, returns objects collected\n",[54,257,258,261],{"class":56,"line":80},[54,259,260],{"class":64},"gc.isenabled()      ",[54,262,263],{"class":86},"# True by default\n",[15,265,266],{},"You rarely call it manually, but forcing a collection is useful after deleting a large web\nof objects, or in tests checking for leaks.",[10,268,270],{"id":269},"generational-collection","Generational collection",[15,272,273,274,277],{},"For efficiency the collector is ",[24,275,276],{},"generational",", based on the observation that most objects\ndie young. New objects start in generation 0, which is scanned often; survivors are promoted\nto older generations scanned progressively less.",[44,279,281],{"className":46,"code":280,"language":48,"meta":49,"style":49},"import gc\ngc.get_count()        # (gen0, gen1, gen2) allocation counters\ngc.get_threshold()    # (700, 10, 10) — thresholds that trigger each generation\n",[51,282,283,289,297],{"__ignoreMap":49},[54,284,285,287],{"class":56,"line":57},[54,286,61],{"class":60},[54,288,247],{"class":64},[54,290,291,294],{"class":56,"line":68},[54,292,293],{"class":64},"gc.get_count()        ",[54,295,296],{"class":86},"# (gen0, gen1, gen2) allocation counters\n",[54,298,299,302],{"class":56,"line":80},[54,300,301],{"class":64},"gc.get_threshold()    ",[54,303,304],{"class":86},"# (700, 10, 10) — thresholds that trigger each generation\n",[15,306,307],{},"This keeps the collector cheap: it spends most effort on short-lived objects and rarely\nre-scans long-lived ones.",[10,309,311,313],{"id":310},"del-and-weak-references",[24,312,112],{}," and weak references",[15,315,316,319,320,322,323,326,327,330],{},[51,317,318],{},"__del__"," runs when an object is about to be destroyed, but don't rely on it for critical\ncleanup — its timing is tied to refcount\u002Fgc, and cycles with ",[51,321,318],{}," were historically\ntricky. For cleanup, prefer context managers. To reference an object ",[24,324,325],{},"without"," keeping it\nalive (avoiding cycles), use ",[51,328,329],{},"weakref",".",[44,332,334],{"className":46,"code":333,"language":48,"meta":49,"style":49},"import weakref\nclass Node: pass\nn = Node()\nref = weakref.ref(n)    # doesn't increase refcount\nref()                   # the Node, or None once n is gone\ndel n\nref()                   # None\n",[51,335,336,343,358,368,381,389,396],{"__ignoreMap":49},[54,337,338,340],{"class":56,"line":57},[54,339,61],{"class":60},[54,341,342],{"class":64}," weakref\n",[54,344,345,348,352,355],{"class":56,"line":68},[54,346,347],{"class":60},"class",[54,349,351],{"class":350},"sScJk"," Node",[54,353,354],{"class":64},": ",[54,356,357],{"class":60},"pass\n",[54,359,360,363,365],{"class":56,"line":80},[54,361,362],{"class":64},"n ",[54,364,74],{"class":60},[54,366,367],{"class":64}," Node()\n",[54,369,370,373,375,378],{"class":56,"line":90},[54,371,372],{"class":64},"ref ",[54,374,74],{"class":60},[54,376,377],{"class":64}," weakref.ref(n)    ",[54,379,380],{"class":86},"# doesn't increase refcount\n",[54,382,383,386],{"class":56,"line":101},[54,384,385],{"class":64},"ref()                   ",[54,387,388],{"class":86},"# the Node, or None once n is gone\n",[54,390,391,393],{"class":56,"line":109},[54,392,112],{"class":60},[54,394,395],{"class":64}," n\n",[54,397,398,400],{"class":56,"line":118},[54,399,385],{"class":64},[54,401,402],{"class":86},"# None\n",[15,404,405],{},"Weak references are the standard tool for caches and parent\u002Fchild links that shouldn't\nprevent collection.",[10,407,409],{"id":408},"practical-implications","Practical implications",[15,411,412,413,416,417,419,420,423],{},"Reference counting means CPython's memory use is predictable and resources free promptly —\nbut it adds per-operation overhead and is part of ",[19,414,415],{},"why"," the GIL exists (refcount updates\nmust be thread-safe). Cycles are handled, just not instantly, so a program that builds many\ncyclic structures may want to tune ",[51,418,222],{}," thresholds or call ",[51,421,422],{},"gc.collect()"," strategically.",[10,425,427],{"id":426},"recap","Recap",[15,429,430,431,433,434,437,438,440,441,443,444,447,448,450,451,455],{},"CPython frees memory with two cooperating systems: ",[24,432,26],{}," frees objects the\ninstant their count hits zero (deterministic, prompt), and a ",[24,435,436],{},"generational cyclic\ncollector"," in the ",[51,439,222],{}," module reclaims reference ",[24,442,136],{}," that counting alone can't. The\ncollector tracks only container objects and focuses effort on young generations since most\nobjects die young. Use ",[24,445,446],{},"context managers"," rather than ",[51,449,318],{}," for cleanup, and\n",[24,452,453],{},[51,454,329],{}," to reference objects without keeping them alive.",[457,458,459],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":49,"searchDepth":68,"depth":68,"links":461},[462,463,464,465,466,467,469,470],{"id":12,"depth":68,"text":13},{"id":34,"depth":68,"text":35},{"id":129,"depth":68,"text":130},{"id":215,"depth":68,"text":216},{"id":269,"depth":68,"text":270},{"id":310,"depth":68,"text":468},"del and weak references",{"id":408,"depth":68,"text":409},{"id":426,"depth":68,"text":427},"How CPython manages memory — reference counting as the primary mechanism, the cyclic garbage collector that handles reference cycles, generational collection, and how to work with the gc module.","hard","md","Python",{},true,"\u002Fblog\u002Fpython-garbage-collection-explained","\u002Fpython\u002Finternals\u002Fgarbage-collection",{"title":5,"description":471},"blog\u002Fpython-garbage-collection-explained","Garbage Collection & Reference Counting","Memory & Internals","internals","2026-06-19","Pn2zbQgAt7tRqQow0wlmZHc6cLBUGatO4uLRmYvjVJQ",1782244092108]