Python28 min read

Python Memory Management

Understand how Python manages memory, when objects are freed, why generators save memory, and how to avoid common memory-heavy patterns.

David Miller
July 27, 2025
5.3k251

Python manages memory automatically, but as your programs grow, you must understand memory basics to avoid crashes and slowdowns.

      This lesson will help you build a strong mental model.
      
      ## The big idea
      
      When you create objects (lists, dicts, class instances), they live in memory.
      Python frees memory when objects are no longer referenced.
      
      Key mechanisms:
      - reference counting
      - garbage collection (for cycles)
      
      ## Reference counting (simple example)
      
      ```python
      a = [1, 2, 3]
      b = a  # now two variables reference the same list
      del a  # list still exists because b still references it
      ```
      
      The object is freed only when references drop to zero.
      
      ## Memory size comparison (list vs tuple)
      
      ```python
      import sys
      
      x = [1, 2, 3, 4, 5]
      y = (1, 2, 3, 4, 5)
      
      print(sys.getsizeof(x))
      print(sys.getsizeof(y))
      ```
      
      Tuples often use less memory because they are immutable and simpler internally.
      
      ## Garbage collection (cycles)
      
      Sometimes objects reference each other in a loop (cycle). Reference counting alone can’t clean that, so Python uses garbage collection.
      
      ```python
      import gc
      
      print(gc.isenabled())
      
      gc.collect()  # force cleanup
      ```
      
      ## Big memory mistake: building huge lists
      
      ```python
      # memory heavy
      nums = [i for i in range(10_000_000)]
      print(nums[0], nums[-1])
      ```
      
      Better: generator (lazy)
      
      ```python
      # memory friendly
      nums = (i for i in range(10_000_000))
      print(next(nums))
      ```
      
      ## Why generators help (concept)
      
      A list stores ALL values.
      A generator produces one value at a time.
      
      ## __slots__ to reduce object memory (advanced but practical)
      
      ```python
      class Person:
          __slots__ = ["name", "age"]
      
          def __init__(self, name, age):
              self.name = name
              self.age = age
      ```
      
      This removes the normal per-object __dict__, saving memory when you create many instances.
      
      ## Weak references (prevent memory leaks)
      
      ```python
      import weakref
      
      class Data:
          def __init__(self, value):
              self.value = value
      
      obj = Data(42)
      ref = weakref.ref(obj)
      
      print(ref().value)
      
      del obj
      print(ref())  # None
      ```
      
      ## Graph: list vs generator memory model
      
      ```mermaid
      flowchart LR
        A[List] --> B[Allocates all items in memory]
        C[Generator] --> D[Produces one item at a time]
        D --> E[Low memory usage]
      ```
      
      ## Key points
      
      - Python frees objects when references end
      - Garbage collector handles cycles
      - Generators reduce memory for large data
      - __slots__ helps when you create many objects
      - Weak references can prevent certain memory issues
      
#Python#Advanced#Memory