Skip to content
CodeKruPro

Your language · Python

Memory and the runtime

How the runtime stores objects, garbage collection, and copies versus references.

Learn: Memory and the Python runtime9 minute read

The runtime is what Python does with your code and data once you press Run: how it executes the code, where values are stored, and how memory is freed. Interviewers ask about it because it explains everyday surprises in test code: why changing a list in one place changes it "everywhere", why a shared JSON template (a ready-made example of a request body) gets dirty between tests, and why a browser can stay open after its Python object is gone.

Step 1: how Python runs code

The interpreter is the program that runs your Python code, and the standard one is CPython. When you run or import a module, CPython compiles the source to bytecode, a set of simple instructions, and its virtual machine runs that bytecode. The bytecode of imported modules is cached in __pycache__ as .pyc files, so the next import is faster. The same .py files run on any system with a Python interpreter. Give each project its own virtual environment (python -m venv .venv) with pinned package versions, so CI runs the same Selenium and pytest versions as your machine.

Step 2: names, objects and copies

Every value, including numbers and strings, is an object on a private heap (the area of memory where objects live) that Python manages. A variable is a name bound to an object (attached to it). Each function call gets a frame (a record of one call) that holds its local names, and the frame goes away when the call ends. So Java's "objects on the heap, locals on the stack" becomes "every object on the heap, names in frames". Recursion means a function calls itself. Very deep recursion is stopped by a counter: past sys.getrecursionlimit(), 1000 by default, Python raises RecursionError (an error that means a function called itself too many times).

Because assignment only binds a name, it never copies. A shallow copy, such as d.copy(), makes a new outer object that still shares the objects inside it (the new dict and the old dict point at the same inner list). copy.deepcopy copies every level.

The names template and alias point to the same dict; a shallow copy is a new dict that shares the inner roles list with the original, and a deep copy has its own inner list.
An alias shares everything, a shallow copy shares the inside, a deep copy shares nothing.
import copy, sys

template = {"roles": ["viewer"]}
alias = template
shallow = template.copy()
deep = copy.deepcopy(template)
template["roles"].append("admin")
print(alias["roles"], shallow["roles"], deep["roles"])
print(alias is template, shallow is template)

l1 = [1]; l2 = l1; l1 += [2]
t1 = (1,); t2 = t1; t1 += (2,)
print(l2, t2)

print(sys.getrecursionlimit())
def dive(n):
    return dive(n + 1)
try:
    dive(0)
except RecursionError:
    print("RecursionError")
['viewer', 'admin'] ['viewer', 'admin'] ['viewer']
True False
[1, 2] (1,)
1000
RecursionError

+= changes a list in place, so l2 sees the change. A tuple cannot change, so += builds a new tuple and only t1 moves to it.

Step 3: reference counting and the garbage collector

CPython frees memory in two ways. First, every object counts the references to it, and when the count drops to zero the object is freed at once. del x removes the name x; the object goes only if nothing else refers to it. Second, a cyclic garbage collector (the gc module) finds groups of objects that refer to each other but that nothing can reach, which counting alone cannot free.

import gc

class Page:
    def __init__(self, name):
        self.name = name
        self.other = None

home, cart = Page("home"), Page("cart")
home.other, cart.other = cart, home      # a reference cycle
del home, cart                           # names gone, objects still in a cycle
print("objects collected:", gc.collect() > 0)
print(len(gc.get_threshold()), "generation thresholds")

pages = [Page("login")]
alias = pages[0]
del pages
print("still alive through alias:", alias.name)
objects collected: True
3 generation thresholds
still alive through alias: login

Python manages memory, not outside resources. Freeing a WebDriver object (the Selenium object that controls a browser) does not reliably close the browser, so call driver.quit() in fixture teardown (the clean-up part of a pytest fixture, a function that prepares things for a test).

Step 4: introspection

Reflection means inspecting and using classes and objects by name while the program runs. Python's version is called introspection. type(obj), isinstance, getattr(obj, "name", default), hasattr, vars(obj) and the inspect module are all built in. pytest (the usual Python test tool) relies on it: it collects functions whose names start with test and reads each function's parameter names, through inspect.signature, to decide which fixtures to pass in.

How to answer in an interview

Explain memory with one example: "b = a gives the list a second name, so b.append(4) shows up in a. For an independent copy I use a.copy(), and copy.deepcopy when the data is nested." For garbage collection, name both parts: reference counting frees objects at once, and the cyclic collector handles reference cycles.

Common mistakes

  • Thinking assignment copies a list or dict.
  • Using a shallow copy of nested test data, then changing the inner list.
  • Believing del x always frees the object.
  • Comparing numbers with is; small ints are cached only as a CPython detail, so is can give different answers for different values.
  • Expecting the garbage collector to quit browsers.

Recap

CPython compiles to bytecode and runs it in its virtual machine. Every object lives on the heap, and names in frames point to them. Assignment shares, shallow copies share the inside, and deep copies share nothing. Reference counting plus a cyclic collector free memory, but you must close files and browsers yourself.

9 questions0/9 solved

StatusQuestionDifficultyAsked in
Not startedTwo names for one listEasyAsked in: Infosys
Not startedHow Python runs your code: interpreter, bytecode and virtual environmentsEasy-MediumAsked in: TCS
Pro+How does Python manage memory and garbage collection?MediumAsked in: Accenture, TCS
Pro+Where do objects and local variables live in Python?MediumAsked in: Capgemini, TCS, Wipro
Pro+Shallow copy or deep copy?MediumAsked in: Infosys, TCS
Pro++= on a list versus a tupleMediumAsked in: Infosys
Pro+Why "is" with numbers is unreliableMediumAsked in: Capgemini, Infosys
Pro+Introspection: the Python version of reflectionMediumAsked in: TCS
Pro+When is an object freed in CPython?MediumAsked in: Accenture, TCS
Not startedNot startedIn progressIn progressSolvedSolvedPro+Pro+

The first 2 questions of each topic are free. Pro+ opens every question.

Back to the plan