[{"data":1,"prerenderedAt":66},["ShallowReactive",2],{"qa-\u002Fpython\u002Finternals\u002Fgarbage-collection":3},{"page":4,"siblings":57,"blog":48},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"related":48,"seo":49,"seoDescription":50,"stem":51,"subtopic":52,"topic":53,"topicSlug":54,"updated":55,"__hash__":56},"qa\u002Fpython\u002Finternals\u002Fgarbage-collection.md","Garbage Collection",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"hard","md","Python","python",{},true,1,"\u002Fpython\u002Finternals\u002Fgarbage-collection",[23,28,32,36,40,44],{"id":24,"difficulty":25,"q":26,"a":27},"reference-counting","medium","How does reference counting work in Python?","Every CPython object carries a **reference count** — the number of references\npointing at it. The count goes **up** when you bind a new name, append it to a\ncontainer, or pass it to a function, and **down** when a name is reassigned, goes\nout of scope, or is `del`'d. When the count hits **zero**, the object is\n**immediately deallocated**.\n\n```python\na = [1, 2, 3]    # the list's refcount is 1\nb = a            # 2 — another reference\ndel a            # 1 — still alive (b references it)\nb = None         # 0 — list is freed right away\n```\n\nThis is **deterministic** and prompt — memory is reclaimed the instant the last\nreference disappears, not at some later sweep.\n\nWhy it matters: reference counting handles the vast majority of garbage, but it\n**can't free reference cycles** on its own, which is why CPython adds a second\ncollector.\n",{"id":29,"difficulty":14,"q":30,"a":31},"cyclic-gc","Why does Python need a cyclic garbage collector?","Reference counting fails on **reference cycles** — objects that refer to each other,\nso their counts never reach zero even when nothing outside the cycle reaches them.\nWithout help they'd leak forever. CPython's **cyclic garbage collector** (the `gc`\nmodule) periodically finds and frees these unreachable cycles.\n\n```python\nimport gc\na, b = {}, {}\na['b'] = b        # a -> b\nb['a'] = a        # b -> a   (a cycle)\ndel a, b          # both refcounts stay at 1 — NOT freed by refcounting\ngc.collect()      # the cyclic collector reclaims the unreachable cycle\n```\n\nThe collector works by tracking **container** objects (lists, dicts, instances) and\ndetecting groups whose only references are internal to the group.\n\nRule of thumb: you rarely call `gc.collect()` manually — it runs automatically —\nbut it's worth knowing cycles are collected **later**, not immediately like refcounts.\n",{"id":33,"difficulty":25,"q":34,"a":35},"sys-getrefcount","What does sys.getrefcount tell you and why is it always higher than expected?","`sys.getrefcount(obj)` returns the object's current **reference count**. It almost\nalways reads **one higher** than you expect because **passing the object as the\nargument** creates a temporary reference for the duration of the call.\n\n```python\nimport sys\nx = []\nsys.getrefcount(x)   # 2 — one for `x`, one for the argument itself\n\ny = x\nsys.getrefcount(x)   # 3 — `x`, `y`, and the argument\n```\n\nSmall ints and interned strings show huge counts because they're **cached\nsingletons** shared across the whole interpreter.\n\nWhy it matters: it's a debugging\u002Fteaching tool for understanding aliasing and leaks\n— just remember to **subtract one** for the call's own reference, and don't rely on\nexact values across implementations.\n",{"id":37,"difficulty":14,"q":38,"a":39},"weak-references","What is a weak reference and when do you use one?","A **weak reference** (`weakref` module) points to an object **without increasing its\nreference count**, so it does **not** keep the object alive. When the last *strong*\nreference is gone, the object is collected and the weak reference returns `None`.\n\n```python\nimport weakref\nclass Node: pass\n\nn = Node()\nr = weakref.ref(n)   # weak — doesn't count toward n's lifetime\nr()                  # \u003CNode object> — still alive\ndel n                # last strong ref gone -> object freed\nr()                  # None — referent is dead\n```\n\nThey're ideal for **caches** and **observer\u002Fparent-child back-references** where you\nwant to reference an object but not prevent its cleanup — `weakref.WeakValueDictionary`\nis a common cache type.\n\nRule of thumb: use a weak reference to **break a cycle** or avoid a memory leak when\none object should not own the lifetime of another.\n",{"id":41,"difficulty":14,"q":42,"a":43},"del-pitfalls","What are the pitfalls of the __del__ method?","`__del__` is a **finalizer** called when an object is about to be destroyed — but\nits timing is **not guaranteed**. It runs only when the refcount hits zero (or later,\nvia the cyclic collector), so you can't rely on **when**, or even **whether**, it\nruns. Note `del obj` only decrements the refcount; it doesn't directly call `__del__`.\n\n```python\nclass Resource:\n    def __del__(self):\n        print(\"cleaning up\")   # may run late, or not at all on interpreter exit\n\nr = Resource()\nr2 = r\ndel r                          # nothing happens — r2 still references it\ndel r2                         # NOW refcount is 0 -> __del__ runs\n```\n\nObjects in a reference cycle that define `__del__` historically couldn't be\ncollected at all (improved in Python 3.4+ via PEP 442), and exceptions raised inside\n`__del__` are **ignored**.\n\nRule of thumb: don't use `__del__` for important cleanup — use **context managers\n(`with`)** or `try\u002Ffinally`, which give deterministic, explicit release.\n",{"id":45,"difficulty":14,"q":46,"a":47},"generational-gc","What is generational garbage collection?","The cyclic collector is **generational**: it sorts tracked objects into **three\ngenerations (0, 1, 2)** based on how many collections they've survived. New objects\nstart in **generation 0**, which is scanned **most frequently**; survivors are\n**promoted** to older generations that are scanned **less often**.\n\nThe idea is the **weak generational hypothesis**: most objects die young, so it's\nefficient to focus collection effort on the youngest generation and rarely re-scan\nlong-lived objects.\n\n```python\nimport gc\ngc.get_count()        # (gen0, gen1, gen2) allocation counters\ngc.get_threshold()    # (700, 10, 10) — triggers per generation\ngc.collect(0)         # collect only generation 0 (cheap, frequent)\n```\n\nA generation-0 collection is fast and common; full (generation-2) collections are\nrarer and more expensive.\n\nRule of thumb: this is mostly automatic, but for latency-sensitive code you can tune\nthresholds, call `gc.collect()` strategically, or `gc.disable()` it if you manage\nlifetimes carefully.\n",null,{"description":11},"Python interview questions on memory management — reference counting, the cyclic garbage collector and reference cycles, sys.getrefcount, weak references, __del__ pitfalls, and generational GC.","python\u002Finternals\u002Fgarbage-collection","Garbage Collection & Reference Counting","Memory & Internals","internals","2026-06-18","FHx3siFM4NKe-jUoUL6tXG3myt5m8Tb5BnZ4L36YsOU",[58,59,62],{"subtopic":52,"path":21,"order":20},{"subtopic":60,"path":61,"order":12},"Identity, is vs ==, & Interning","\u002Fpython\u002Finternals\u002Fidentity-interning",{"subtopic":63,"path":64,"order":65},"The CPython Execution Model","\u002Fpython\u002Finternals\u002Fcpython-model",3,1781808681277]