Skip to content

Memory



Why we need to talk about memory

Analogy

  • Picture a wall covered in identical lockers, stretching further than you can see. Every locker is the same size, and every one has a number painted on it, starting at 0.
  • That is your computer's memory. Nothing more clever than that.
  • Everything your program touches lives in one of those lockers: every number, every name, every list, every function currently running.
  • When people say a program uses 200 MB, they mean it has taken over 200 million of these lockers.

This is the chapter that makes the previous seven click into place. It is also the reason you can understand why an array is fast, instead of memorising that it is.


Bits, bytes, and where types come from

Definition

Each locker holds one byte, which is 8 bits, and a bit is a single 0 or 1. That is all memory can store.

Eight bits can be arranged 256 ways, which is enough for one English character. Bigger things need more lockers side by side.

TypeBytesWhy
char1256 possible symbols is enough
int4Enough for about plus or minus 2.1 billion
double8Needs the room for precision
boolean1Needs 1 bit, but memory is handed out by the byte

Now go back to data types. An integer overflows because it only got 4 lockers, and 4 lockers can only count so high. A double is more precise than a float because it was given twice the space.

A data type is really just a decision about how many lockers to reserve and how to read what is inside them.


Two areas that behave differently

Your program does not treat memory as one big pile. It splits it in two.

The stack is small, fast and completely automatic. It holds local variables and the record of which functions are currently running. Call a function and a frame is pushed on top holding that function's locals; the function finishes and its frame is popped off, and everything inside it disappears instantly.

That is why local variables vanish when a function ends. It is not a rule somebody invented. It is what happens when the frame is popped.

Note

And now stack overflow makes sense. Recursion with no base case keeps calling itself, pushing frame after frame and never popping any. The stack is small, it fills up, and the program dies. The error is named after the data structure that ran out of room.

The heap is large, flexible and slower. It holds things whose size is not known in advance, or that need to outlive the function that created them: lists, objects, strings.

The stackThe heap
SizeSmall and fixedLarge
SpeedVery fastSlower
HoldsLocals and function framesObjects, lists, dynamic data
CleanupAutomatic when the function exitsManual, or a garbage collector

The mystery from the variables chapter

Here is that confusing example again, and now you have the tools for it.

Code
x = [1, 2, 3]y = xx.append(4)print(y)      # [1, 2, 3, 4]

The list itself lives on the heap. The variable x does not hold the list - it holds the list's address, and that address sits on the stack. So y = x copied the address. Two name tags, one box.

A plain number is small and fixed in size, so it sits directly on the stack and b = a copies the actual value. Two separate boxes, no connection.

One sentence: small values are copied, big things are shared by address. When you genuinely want a separate copy of a list you have to ask for one, with y = x.copy() or the equivalent.


Why an array is fast

This is the payoff. Everything above was building to it.

An array: one continuous run of lockers10100002010041301008240101235010164indexaddress1000 + (3 x 4) = 1012, one step, any indexA linked list: nodes anywhere, joined by addresses1020400201284130312024019963No formula here: the addresses are not evenly spaced, so index 3 means following three links.

An array's items sit in one continuous run of lockers, back to back with no gaps. Each int takes 4 bytes, so each item is exactly 4 addresses further along. Which means when you ask for arr[3] the computer does not go looking for it. It calculates:

Code
address = start + (index x size)address = 1000 + (3 x 4)address = 1012

One multiplication, one addition, done. It takes the same time to reach item 3 as item 3,000,000.

That is what O(1) access means. Not that the computer is clever. Just arithmetic on an address.

A linked list's nodes are scattered anywhere on the heap, joined by addresses. There is no formula, because the nodes are not evenly spaced, so to reach the fourth node you start at the head and follow three links. That is O(n), and it is O(n) because of physical layout rather than a rule in a textbook.

Note

There is a second, quieter reason arrays are fast. The processor pulls memory in chunks, so when it fetches one array item it grabs the neighbours too and keeps them close by. The next few items are already there when you need them. Scattered nodes get none of that.


Cleaning up

Stack memory handles itself. Heap memory has to be released, and languages take two approaches.

Manual (C, C++): you allocate, you free. Full control and full responsibility.

Automatic (Java, Python, JavaScript, Go): a garbage collector runs in the background, finds heap objects nothing points to any more, and reclaims them. Easier and safer, at the cost of a small occasional pause.

Two problems worth knowing by name. A memory leak is heap memory you allocated, never released, and no longer have a reference to - occupied but unreachable. A dangling pointer is memory you freed but kept using the address of; the locker belongs to something else now.


Why this chapter matters for everything next

Every claim in a data structures course rests on this page.

  • Arrays give O(1) access because items sit in a continuous block and the address is a calculation
  • Linked lists are O(n) to search because nodes are scattered and you must follow addresses one at a time
  • Inserting into an array is O(n) because continuous memory means everything after the gap has to physically shift
  • Inserting into a linked list is cheap because nothing moves, you just rewrite two addresses
  • Recursion crashes with stack overflow because the stack is small and every call pushes a frame

None of those are facts to memorise. They all fall out of how memory is physically arranged.


Mistakes to watch for

Watch out

  • Thinking a variable holds a list. It holds the address of one.
  • Assuming y = x copies a list. It copies the address.
  • Confusing the stack data structure with the memory stack. They share a name because they work the same way.
  • Believing garbage collection makes leaks impossible. Hold a reference to something you no longer need and it never gets collected.
  • Thinking memory is unlimited. Stack space especially is small.

Quick recap

  • Memory is a long line of numbered, byte-sized lockers
  • A data type decides how many to reserve and how to read them
  • The stack is small, fast and automatic. It holds locals and function frames
  • The heap is large and flexible. It holds objects and lists
  • Small values are copied. Big things are shared through their address
  • Arrays are fast because their items are continuous and the address is a calculation

Key takeaway

Memory is numbered lockers. Everything you learn about speed later is really a question of whether your data sits together or scattered apart.