Chapter 4: Complexity, Memory and Performance Analysis
4.08Beginner9 topics20 min5 interactive animations

Computer memory model

How a Java program sees memory: stack frames and the call stack, heap objects and their headers, references, garbage collection, and how CPU caches and cache lines make some access orders much faster than others.

Learning goals

  • Explain where locals, objects and arrays live
  • Estimate object sizes on a 64-bit HotSpot JVM
  • Explain cache lines and why traversal order matters

Prerequisites

Space complexityJava language essentials
4.08.1

Stack frames

Every method call pushes a stack frame holding its parameters, local variables, a link to the caller and the return address. Primitives live directly in the frame; for objects the frame holds only the reference. When the method returns, its frame disappears and the memory is reused immediately.

Frames are cheap to create, but each one counts toward the depth limit of the thread's stack.

Factorial: calls, base case and return path

Interactive · edit the input

Your input

Try

Recursion tree · 1 call

factorial(4)…
Running now
Call

Call factorial(4). It is frame number 1 on the call stack.

Step 1 / 20
Java
1static long factorial(int n) {
2 if (n == 0) return 1; // base case
3 return n * factorial(n - 1); // recursive case
4}

Live state

1 calls

n

4

depth

1

No condition evaluated on this step

Call stack (top first)

  1. factorial(4)
Execution trace (dry run table)Show 20 steps
StepLineStateWhat happens
1L1n=4 depth=1Call factorial(4). It is frame number 1 on the call stack.
2L2n=4n is not 0, so this call cannot answer yet. It needs factorial(3).
3L3n=4Pause factorial(4) and call factorial(3). The pending multiplication by 4 waits on the stack.
4L1n=3 depth=2Call factorial(3). It is frame number 2 on the call stack.
5L2n=3n is not 0, so this call cannot answer yet. It needs factorial(2).
6L3n=3Pause factorial(3) and call factorial(2). The pending multiplication by 3 waits on the stack.
7L1n=2 depth=3Call factorial(2). It is frame number 3 on the call stack.
8L2n=2n is not 0, so this call cannot answer yet. It needs factorial(1).
9L3n=2Pause factorial(2) and call factorial(1). The pending multiplication by 2 waits on the stack.
10L1n=1 depth=4Call factorial(1). It is frame number 4 on the call stack.
11L2n=1n is not 0, so this call cannot answer yet. It needs factorial(0).
12L3n=1Pause factorial(1) and call factorial(0). The pending multiplication by 1 waits on the stack.
13L1n=0 depth=5Call factorial(0). It is frame number 5 on the call stack.
14L2n=0Base case reached: no more recursion. Everything below this point is the return path.
15L2n=0 return=1return 1.
16L3n=1 return=1factorial(1) resumes: 1 × 1 = 1. Return it to the caller.
17L3n=2 return=2factorial(2) resumes: 2 × 1 = 2. Return it to the caller.
18L3n=3 return=6factorial(3) resumes: 3 × 2 = 6. Return it to the caller.
19L3n=4 return=24factorial(4) resumes: 4 × 6 = 24. Return it to the caller.
20L3return=24factorial(4) = 24, using 5 calls and a maximum stack depth of 5.
4.08.2

Heap objects

Objects and arrays are allocated on the heap, shared by all threads, and live until the garbage collector finds them unreachable. Allocation is fast (a pointer bump in the young generation), but heap objects need headers, references to reach them and the collector's attention.

Locals that point to heap objects are references; the object may outlive the method if another reference keeps it.

Java
1int x = 5; // primitive: stored in the stack frame
2int[] a = new int[1000]; // reference in the frame, 1000 ints on the heap
3List<Integer> xs = new ArrayList<>(); // reference + ArrayList object + backing Object[]
4.08.3

Call stack

The call stack is the chain of active frames, from main to the method running now. Recursion adds one frame per call, and the stack unwinds as calls return. A StackOverflowError means the chain exceeded the stack's size, usually because of a missing base case or a recursion deeper than about 10⁴ to 10⁵ frames.

Stack traces print this chain, innermost call first.

Java
1static int depth(int n) { return depth(n + 1); } // no base case
2// depth(0) -> StackOverflowError after roughly 10,000-20,000 frames with the default stack

Factorial: calls, base case and return path

Interactive · edit the input

Your input

Try

Recursion tree · 1 call

factorial(4)…
Running now
Call

Call factorial(4). It is frame number 1 on the call stack.

Step 1 / 20
Java
1static long factorial(int n) {
2 if (n == 0) return 1; // base case
3 return n * factorial(n - 1); // recursive case
4}

Live state

1 calls

n

4

depth

1

No condition evaluated on this step

Call stack (top first)

  1. factorial(4)
Execution trace (dry run table)Show 20 steps
StepLineStateWhat happens
1L1n=4 depth=1Call factorial(4). It is frame number 1 on the call stack.
2L2n=4n is not 0, so this call cannot answer yet. It needs factorial(3).
3L3n=4Pause factorial(4) and call factorial(3). The pending multiplication by 4 waits on the stack.
4L1n=3 depth=2Call factorial(3). It is frame number 2 on the call stack.
5L2n=3n is not 0, so this call cannot answer yet. It needs factorial(2).
6L3n=3Pause factorial(3) and call factorial(2). The pending multiplication by 3 waits on the stack.
7L1n=2 depth=3Call factorial(2). It is frame number 3 on the call stack.
8L2n=2n is not 0, so this call cannot answer yet. It needs factorial(1).
9L3n=2Pause factorial(2) and call factorial(1). The pending multiplication by 2 waits on the stack.
10L1n=1 depth=4Call factorial(1). It is frame number 4 on the call stack.
11L2n=1n is not 0, so this call cannot answer yet. It needs factorial(0).
12L3n=1Pause factorial(1) and call factorial(0). The pending multiplication by 1 waits on the stack.
13L1n=0 depth=5Call factorial(0). It is frame number 5 on the call stack.
14L2n=0Base case reached: no more recursion. Everything below this point is the return path.
15L2n=0 return=1return 1.
16L3n=1 return=1factorial(1) resumes: 1 × 1 = 1. Return it to the caller.
17L3n=2 return=2factorial(2) resumes: 2 × 1 = 2. Return it to the caller.
18L3n=3 return=6factorial(3) resumes: 3 × 2 = 6. Return it to the caller.
19L3n=4 return=24factorial(4) resumes: 4 × 6 = 24. Return it to the caller.
20L3return=24factorial(4) = 24, using 5 calls and a maximum stack depth of 5.
4.08.4

References

A reference is a small value (4 bytes with compressed object pointers) that identifies an object. Assigning a reference copies the address, not the object, which is why aliasing happens and why a List<Integer> costs a reference plus an Integer object per element.

Step through the alias, clone and deep-copy cases to see where references are shared.

Aliasing and copying arrays

Interactive · edit the input

Your input

Try

variables

a → @1

@1 int[]

100
201
302
403
Setup

new int[] creates one object on the heap; the variable a holds only a reference to it.

Step 1 / 4
Java
1int[] a = { 10, 20, 30, 40 }; // options 0 and 1 use this 1D array
2int[] b = a; // option 0: copies the reference only
3int[] b = a.clone(); // option 1: copies the elements
4int[][] a2 = { { 10, 20 }, { 30, 40 } }; // options 2 and 3 use this 2D array
5int[][] b2 = a2.clone(); // option 2: new outer array, SAME row objects
6int[][] b2 = Arrays.stream(a2).map(int[]::clone).toArray(int[][]::new); // option 3: rows copied too
7b[idx] = v; // (b2[0][idx] = v for options 2 and 3)
8System.out.println(Arrays.toString(a)); // does a see the change?

Live state

0 writes

a.length

4

No condition evaluated on this step
Execution trace (dry run table)Show 4 steps
StepLineStateWhat happens
1L1a.length=4new int[] creates one object on the heap; the variable a holds only a reference to it.
2L2same=trueb = a copies the reference, not the array. Both variables now point at the same object (@1).
3L7idx=1 v=99b[1] = 99 writes into the object b points at.
4L8a=[10, 99, 30, 40] b=[10, 99, 30, 40]a shows the change too ([10, 99, 30, 40]), because a and b are two names for one object.
4.08.5

Object headers

Every object starts with a header: a mark word (identity hash, lock and GC bits) and a class pointer, 12 bytes on a 64-bit HotSpot JVM with compressed class pointers. Arrays add a 4-byte length for a 16-byte header. Object sizes are rounded up to a multiple of 8 bytes, so tiny objects have large overhead.

That is why an int[] of one million ints takes about 4 MB while a List<Integer> of the same values takes about 20 MB.

Object (typical 64-bit HotSpot)Approx. size
new Object()16 bytes
Integer16 bytes
Long, Double24 bytes
int[n]16 + 4n, rounded up to 8
long[n]16 + 8n
ArrayList<Integer> of n valuesabout 4n (references) + 16n (Integer objects) + header
4.08.6

Garbage collection

The garbage collector reclaims heap objects that are no longer reachable from any live reference. Most objects die young, so the heap is split into young and old generations: minor collections clean the young generation quickly, and rarer major collections handle the old one. Collectors (G1, ZGC, Parallel) trade pause time against throughput.

For algorithm code the practical rule is to avoid allocating in hot loops. See the Java architecture chapter for the collectors in detail.

4.08.7

Cache locality

Memory is read in blocks. Accessing data close in memory (spatial locality) or reusing recently used data (temporal locality) is fast, because it is already in the cache. Walking a 2D array row by row reads consecutive addresses; walking column by column jumps a whole row each step.

The simulator below uses 4-int lines and a small LRU cache so you can count hits and misses for row-major versus column-major order.

Cache lines: row-major vs column-major traversal

Interactive · edit the input

Your input

Try

memory (row-major, 4 ints per cache line)

0line 0
1line 0
2line 0
3line 0
4line 1
5line 1
6line 1
7line 1
8line 2
9line 2
10line 2
11line 2
12line 3
13line 3
14line 3
15line 3
16line 4
17line 4
18line 4
19line 4
20line 5
21line 5
22line 5
23line 5
24line 6
25line 6
26line 6
27line 6
28line 7
29line 7
30line 7
31line 7

cache (2 lines, LRU)

empty
Setup

A 4×8 matrix is stored row by row in one long run of memory. The CPU fetches memory in cache lines of 4 ints and keeps the 2 most recently used lines.

Step 1 / 34
Java
1int sumRowMajor(int[][] m) { // inner loop walks along a row
2 int sum = 0;
3 for (int r = 0; r < m.length; r++)
4 for (int c = 0; c < m[0].length; c++) sum += m[r][c];
5 return sum;
6}
7int sumColumnMajor(int[][] m) { // inner loop jumps down a column
8 int sum = 0;
9 for (int c = 0; c < m[0].length; c++)
10 for (int r = 0; r < m.length; r++) sum += m[r][c];
11 return sum;
12}

Live state

0 hits0 misses

rows

4

cols

8

lineSize

4

cache lines

2

No condition evaluated on this step
Execution trace (dry run table)Show 34 steps
StepLineStateWhat happens
1L3rows=4 cols=8 lineSize=4 cache lines=2A 4×8 matrix is stored row by row in one long run of memory. The CPU fetches memory in cache lines of 4 ints and keeps the 2 most recently used lines.
2L4r=0 c=0 address=0 line=0 hits=0 misses=1m[0][0] is memory cell 0, on line 0, which is not cached: a miss. The CPU stalls while the whole line is loaded.
3L4r=0 c=1 address=1 line=0 hits=1 misses=1m[0][1] is memory cell 1, on line 0, which is already in the cache: a hit (fast).
4L4r=0 c=2 address=2 line=0 hits=2 misses=1m[0][2] is memory cell 2, on line 0, which is already in the cache: a hit (fast).
5L4r=0 c=3 address=3 line=0 hits=3 misses=1m[0][3] is memory cell 3, on line 0, which is already in the cache: a hit (fast).
6L4r=0 c=4 address=4 line=1 hits=3 misses=2m[0][4] is memory cell 4, on line 1, which is not cached: a miss. The CPU stalls while the whole line is loaded.
7L4r=0 c=5 address=5 line=1 hits=4 misses=2m[0][5] is memory cell 5, on line 1, which is already in the cache: a hit (fast).
8L4r=0 c=6 address=6 line=1 hits=5 misses=2m[0][6] is memory cell 6, on line 1, which is already in the cache: a hit (fast).
9L4r=0 c=7 address=7 line=1 hits=6 misses=2m[0][7] is memory cell 7, on line 1, which is already in the cache: a hit (fast).
10L4r=1 c=0 address=8 line=2 hits=6 misses=3m[1][0] is memory cell 8, on line 2, which is not cached: a miss. The CPU stalls while the whole line is loaded.
11L4r=1 c=1 address=9 line=2 hits=7 misses=3m[1][1] is memory cell 9, on line 2, which is already in the cache: a hit (fast).
12L4r=1 c=2 address=10 line=2 hits=8 misses=3m[1][2] is memory cell 10, on line 2, which is already in the cache: a hit (fast).
13L4r=1 c=3 address=11 line=2 hits=9 misses=3m[1][3] is memory cell 11, on line 2, which is already in the cache: a hit (fast).
14L4r=1 c=4 address=12 line=3 hits=9 misses=4m[1][4] is memory cell 12, on line 3, which is not cached: a miss. The CPU stalls while the whole line is loaded.
15L4r=1 c=5 address=13 line=3 hits=10 misses=4m[1][5] is memory cell 13, on line 3, which is already in the cache: a hit (fast).
16L4r=1 c=6 address=14 line=3 hits=11 misses=4m[1][6] is memory cell 14, on line 3, which is already in the cache: a hit (fast).
17L4r=1 c=7 address=15 line=3 hits=12 misses=4m[1][7] is memory cell 15, on line 3, which is already in the cache: a hit (fast).
18L4r=2 c=0 address=16 line=4 hits=12 misses=5m[2][0] is memory cell 16, on line 4, which is not cached: a miss. The CPU stalls while the whole line is loaded.
19L4r=2 c=1 address=17 line=4 hits=13 misses=5m[2][1] is memory cell 17, on line 4, which is already in the cache: a hit (fast).
20L4r=2 c=2 address=18 line=4 hits=14 misses=5m[2][2] is memory cell 18, on line 4, which is already in the cache: a hit (fast).
21L4r=2 c=3 address=19 line=4 hits=15 misses=5m[2][3] is memory cell 19, on line 4, which is already in the cache: a hit (fast).
22L4r=2 c=4 address=20 line=5 hits=15 misses=6m[2][4] is memory cell 20, on line 5, which is not cached: a miss. The CPU stalls while the whole line is loaded.
23L4r=2 c=5 address=21 line=5 hits=16 misses=6m[2][5] is memory cell 21, on line 5, which is already in the cache: a hit (fast).
24L4r=2 c=6 address=22 line=5 hits=17 misses=6m[2][6] is memory cell 22, on line 5, which is already in the cache: a hit (fast).
25L4r=2 c=7 address=23 line=5 hits=18 misses=6m[2][7] is memory cell 23, on line 5, which is already in the cache: a hit (fast).
26L4r=3 c=0 address=24 line=6 hits=18 misses=7m[3][0] is memory cell 24, on line 6, which is not cached: a miss. The CPU stalls while the whole line is loaded.
27L4r=3 c=1 address=25 line=6 hits=19 misses=7m[3][1] is memory cell 25, on line 6, which is already in the cache: a hit (fast).
28L4r=3 c=2 address=26 line=6 hits=20 misses=7m[3][2] is memory cell 26, on line 6, which is already in the cache: a hit (fast).
29L4r=3 c=3 address=27 line=6 hits=21 misses=7m[3][3] is memory cell 27, on line 6, which is already in the cache: a hit (fast).
30L4r=3 c=4 address=28 line=7 hits=21 misses=8m[3][4] is memory cell 28, on line 7, which is not cached: a miss. The CPU stalls while the whole line is loaded.
31L4r=3 c=5 address=29 line=7 hits=22 misses=8m[3][5] is memory cell 29, on line 7, which is already in the cache: a hit (fast).
32L4r=3 c=6 address=30 line=7 hits=23 misses=8m[3][6] is memory cell 30, on line 7, which is already in the cache: a hit (fast).
33L4r=3 c=7 address=31 line=7 hits=24 misses=8m[3][7] is memory cell 31, on line 7, which is already in the cache: a hit (fast).
34L5accesses=32 hits=24 misses=8 hit rate %=75 return=88 misss and 24 hits (75% hit rate). Both loops do identical arithmetic and are O(rows × cols), but walking along a row uses every int in a loaded line before moving on. Switch the order and compare.
4.08.8

CPU caches

CPUs have small, fast caches between the cores and main memory: typically L1 (tens of KB, about 1 ns), L2 (hundreds of KB to a few MB, a few ns), and a shared L3 (many MB, ten-odd ns), against about 100 ns for main memory. A miss at every level costs roughly a hundred times a hit in L1.

Exact sizes and latencies vary by processor, but the ratios are what matter for algorithm design.

LevelTypical sizeTypical latency
L132-64 KB per coreabout 1 ns
L2256 KB-2 MBabout 3-5 ns
L38-64 MB sharedabout 10-20 ns
Main memoryGBsabout 60-100 ns
4.08.9

Cache lines

Data moves between memory and cache in cache lines, typically 64 bytes: 16 ints or 8 longs. Reading a[0] loads a[0..15] together, so the next 15 reads are free hits. A linked list node is a separate object, so each hop may load a new line.

Structure data so values used together sit next to each other; arrays of primitives are the most cache-friendly layout Java offers.

Java
1int n = 4096; int[][] m = new int[n][n]; long s = 0;
2for (int r = 0; r < n; r++) for (int c = 0; c < n; c++) s += m[r][c]; // row order: sequential access
3for (int c = 0; c < n; c++) for (int r = 0; r < n; r++) s += m[r][c]; // column order: a new line each step

Cache lines: row-major vs column-major traversal

Interactive · edit the input

Your input

Try

memory (row-major, 4 ints per cache line)

0line 0
1line 0
2line 0
3line 0
4line 1
5line 1
6line 1
7line 1
8line 2
9line 2
10line 2
11line 2
12line 3
13line 3
14line 3
15line 3
16line 4
17line 4
18line 4
19line 4
20line 5
21line 5
22line 5
23line 5
24line 6
25line 6
26line 6
27line 6
28line 7
29line 7
30line 7
31line 7

cache (2 lines, LRU)

empty
Setup

A 4×8 matrix is stored row by row in one long run of memory. The CPU fetches memory in cache lines of 4 ints and keeps the 2 most recently used lines.

Step 1 / 34
Java
1int sumRowMajor(int[][] m) { // inner loop walks along a row
2 int sum = 0;
3 for (int r = 0; r < m.length; r++)
4 for (int c = 0; c < m[0].length; c++) sum += m[r][c];
5 return sum;
6}
7int sumColumnMajor(int[][] m) { // inner loop jumps down a column
8 int sum = 0;
9 for (int c = 0; c < m[0].length; c++)
10 for (int r = 0; r < m.length; r++) sum += m[r][c];
11 return sum;
12}

Live state

0 hits0 misses

rows

4

cols

8

lineSize

4

cache lines

2

No condition evaluated on this step
Execution trace (dry run table)Show 34 steps
StepLineStateWhat happens
1L3rows=4 cols=8 lineSize=4 cache lines=2A 4×8 matrix is stored row by row in one long run of memory. The CPU fetches memory in cache lines of 4 ints and keeps the 2 most recently used lines.
2L4r=0 c=0 address=0 line=0 hits=0 misses=1m[0][0] is memory cell 0, on line 0, which is not cached: a miss. The CPU stalls while the whole line is loaded.
3L4r=0 c=1 address=1 line=0 hits=1 misses=1m[0][1] is memory cell 1, on line 0, which is already in the cache: a hit (fast).
4L4r=0 c=2 address=2 line=0 hits=2 misses=1m[0][2] is memory cell 2, on line 0, which is already in the cache: a hit (fast).
5L4r=0 c=3 address=3 line=0 hits=3 misses=1m[0][3] is memory cell 3, on line 0, which is already in the cache: a hit (fast).
6L4r=0 c=4 address=4 line=1 hits=3 misses=2m[0][4] is memory cell 4, on line 1, which is not cached: a miss. The CPU stalls while the whole line is loaded.
7L4r=0 c=5 address=5 line=1 hits=4 misses=2m[0][5] is memory cell 5, on line 1, which is already in the cache: a hit (fast).
8L4r=0 c=6 address=6 line=1 hits=5 misses=2m[0][6] is memory cell 6, on line 1, which is already in the cache: a hit (fast).
9L4r=0 c=7 address=7 line=1 hits=6 misses=2m[0][7] is memory cell 7, on line 1, which is already in the cache: a hit (fast).
10L4r=1 c=0 address=8 line=2 hits=6 misses=3m[1][0] is memory cell 8, on line 2, which is not cached: a miss. The CPU stalls while the whole line is loaded.
11L4r=1 c=1 address=9 line=2 hits=7 misses=3m[1][1] is memory cell 9, on line 2, which is already in the cache: a hit (fast).
12L4r=1 c=2 address=10 line=2 hits=8 misses=3m[1][2] is memory cell 10, on line 2, which is already in the cache: a hit (fast).
13L4r=1 c=3 address=11 line=2 hits=9 misses=3m[1][3] is memory cell 11, on line 2, which is already in the cache: a hit (fast).
14L4r=1 c=4 address=12 line=3 hits=9 misses=4m[1][4] is memory cell 12, on line 3, which is not cached: a miss. The CPU stalls while the whole line is loaded.
15L4r=1 c=5 address=13 line=3 hits=10 misses=4m[1][5] is memory cell 13, on line 3, which is already in the cache: a hit (fast).
16L4r=1 c=6 address=14 line=3 hits=11 misses=4m[1][6] is memory cell 14, on line 3, which is already in the cache: a hit (fast).
17L4r=1 c=7 address=15 line=3 hits=12 misses=4m[1][7] is memory cell 15, on line 3, which is already in the cache: a hit (fast).
18L4r=2 c=0 address=16 line=4 hits=12 misses=5m[2][0] is memory cell 16, on line 4, which is not cached: a miss. The CPU stalls while the whole line is loaded.
19L4r=2 c=1 address=17 line=4 hits=13 misses=5m[2][1] is memory cell 17, on line 4, which is already in the cache: a hit (fast).
20L4r=2 c=2 address=18 line=4 hits=14 misses=5m[2][2] is memory cell 18, on line 4, which is already in the cache: a hit (fast).
21L4r=2 c=3 address=19 line=4 hits=15 misses=5m[2][3] is memory cell 19, on line 4, which is already in the cache: a hit (fast).
22L4r=2 c=4 address=20 line=5 hits=15 misses=6m[2][4] is memory cell 20, on line 5, which is not cached: a miss. The CPU stalls while the whole line is loaded.
23L4r=2 c=5 address=21 line=5 hits=16 misses=6m[2][5] is memory cell 21, on line 5, which is already in the cache: a hit (fast).
24L4r=2 c=6 address=22 line=5 hits=17 misses=6m[2][6] is memory cell 22, on line 5, which is already in the cache: a hit (fast).
25L4r=2 c=7 address=23 line=5 hits=18 misses=6m[2][7] is memory cell 23, on line 5, which is already in the cache: a hit (fast).
26L4r=3 c=0 address=24 line=6 hits=18 misses=7m[3][0] is memory cell 24, on line 6, which is not cached: a miss. The CPU stalls while the whole line is loaded.
27L4r=3 c=1 address=25 line=6 hits=19 misses=7m[3][1] is memory cell 25, on line 6, which is already in the cache: a hit (fast).
28L4r=3 c=2 address=26 line=6 hits=20 misses=7m[3][2] is memory cell 26, on line 6, which is already in the cache: a hit (fast).
29L4r=3 c=3 address=27 line=6 hits=21 misses=7m[3][3] is memory cell 27, on line 6, which is already in the cache: a hit (fast).
30L4r=3 c=4 address=28 line=7 hits=21 misses=8m[3][4] is memory cell 28, on line 7, which is not cached: a miss. The CPU stalls while the whole line is loaded.
31L4r=3 c=5 address=29 line=7 hits=22 misses=8m[3][5] is memory cell 29, on line 7, which is already in the cache: a hit (fast).
32L4r=3 c=6 address=30 line=7 hits=23 misses=8m[3][6] is memory cell 30, on line 7, which is already in the cache: a hit (fast).
33L4r=3 c=7 address=31 line=7 hits=24 misses=8m[3][7] is memory cell 31, on line 7, which is already in the cache: a hit (fast).
34L5accesses=32 hits=24 misses=8 hit rate %=75 return=88 misss and 24 hits (75% hit rate). Both loops do identical arithmetic and are O(rows × cols), but walking along a row uses every int in a loaded line before moving on. Switch the order and compare.

Practice ladder

Now try it yourself

Easy

  • Where are a local int and an array object stored?
  • What is the approximate size of new Integer values in a list of 10⁶ elements?

Medium

  • Explain what happens to the call stack during factorial(4)
  • Estimate how many cache lines a sequential read of 10⁶ ints touches

Advanced

  • Predict and then measure the difference between row-major and column-major traversal of a 4096 × 4096 int matrix

Key takeaways

  • Primitives and references live in stack frames; objects and arrays live on the heap with a 12-16 byte header.
  • Sequential access uses cache lines fully; scattered access wastes them.
  • Garbage collection frees unreachable objects; fewer allocations mean less work for it.