Hello World

Be Happy!

Memory Interview Q&A — Developer Level


Memory Interview Questions & Answers

Senior developer interview questions on memory management — with good and great answers.

Q1: Difference between stack and heap?

Good answer: Stack is automatic (allocated on function entry, freed on return). Heap is dynamic (malloc/new, freed manually or by GC). Stack is faster.

Great answer adds: The real performance difference is cache locality. Stack data is contiguous → stays in L1 cache (~1ns). Heap allocations scatter across memory → cache misses (~100ns). Also, each thread owns its stack but shares the heap — requiring synchronization.

Q2: What happens when you call malloc()?

Good answer: Searches free list for a block. If not found, asks OS via brk()/mmap().

Great answer adds: Modern allocators (jemalloc, tcmalloc) use per-thread caches and size classes for O(1) allocation. free() doesn't always return memory to OS — keeps it in free list. This is why RSS stays high after freeing.

Q3: What is a stack overflow?

Good answer: Stack exceeds its limit (~1-8MB). Usually from deep/infinite recursion.

Great answer adds:

void recursive(int n) {
    int buffer[1024];     // 4KB per frame
    recursive(n + 1);     // never returns!
}
// 2048 calls × 4KB = 8MB → overflow

// Also: stack overflow is exploitable (buffer overflow attacks)
// Protections: stack canaries, ASLR, NX bit

Q4: What is a memory leak?

Good answer: Heap memory allocated but never freed, with no pointer referencing it.

Great answer adds:

void leak() {
    char *buf = malloc(1024);
    buf = malloc(2048);  // ← first 1024 leaked!
    free(buf);           // only frees 2048
}

// In GC languages: "leaks" are unintentional references
// (growing list, event listeners, closures capturing vars)

// Tools: Valgrind, ASan, pprof (Go), heap dumps (Java)

Q5: What is virtual memory?

Good answer: Each process gets its own address space. OS+MMU translates virtual → physical addresses. Provides isolation and overcommit.

Great answer adds: Three benefits: (1) isolation, (2) simplification (linker doesn't need physical layout), (3) overcommit. Also enables copy-on-write: fork() shares pages until write — then only that page copies. This is how Rails Puma workers share memory.

Q6: Is a page fault always bad?

Good answer: No. Minor faults (first touch of allocated memory) are normal and cheap (~1μs). Major faults (page swapped to disk) are expensive (~10ms).

Great answer adds:

char *buf = malloc(1GB);  // no physical RAM used yet!
buf[0] = 'x';            // minor fault → maps 4KB page
// Only 4KB used, not 1GB! (overcommit)

// This is why malloc(1TB) succeeds on 16GB machine
// — as long as you don't touch it all

Q7: Explain cache locality

Good answer: Accessing nearby memory addresses. CPU loads 64-byte cache lines, so nearby accesses are free.

Great answer adds:

// Same algorithm, same data, 30× speed difference:
for (i=0; i<N; i++) sum += array[i];          // ~10ms (sequential)
for (i=0; i<N; i++) sum += array[random[i]];  // ~300ms (random)

// This is why:
// - Arrays beat linked lists for iteration
// - Column-major vs row-major matters 10×
// - Game engines use Struct-of-Arrays
// - Databases use columnar storage

Q8: What is false sharing?

Good answer: Two threads on different cores modify different variables on the same cache line. The line bounces between cores — 10-50× slowdown.

Great answer adds:

// Bad: both counters on same 64-byte cache line
var counters [2]int64  // 16 bytes, one cache line
// Thread 1: counters[0]++  → invalidates Core 2's line
// Thread 2: counters[1]++  → invalidates Core 1's line

// Fix: padding
type PaddedCounter struct {
    value int64
    _pad  [56]byte  // force separate cache lines
}

// Java: @Contended annotation
// Linux kernel: ____cacheline_aligned

Q9: Process vs thread — memory perspective?

Good answer: Processes have separate address spaces. Threads share heap/globals but have own stacks.

Great answer adds: Thread creation is cheaper (~μs vs ~ms) because no page table copy. Thread bugs are easier (shared memory = race conditions). Process crash is isolated; thread crash kills all sibling threads. Go chose shared-memory + channels; Erlang chose isolated processes + message passing.

Q10: How does Go's escape analysis work?

// Stack (doesn't escape):
func sum() int {
    x := 42           // lives and dies here
    return x           // value copied
}

// Heap (escapes):
func newUser() *User {
    u := User{Name: "Alice"}  // must survive return
    return &u                  // pointer escapes → heap
}

// Check: go build -gcflags="-m"
// Stack alloc = free (no GC pressure)
// Hot path optimization: avoid escape = eliminate GC pauses

Q11: Concurrency vs parallelism — memory perspective?

Parallelism causes:

  • Visibility: Core 0 writes x=5, Core 1 still sees x=0 (needs memory barrier)
  • Atomicity: 64-bit write might not be atomic on 32-bit CPU
  • Ordering: CPU reorders reads/writes (memory model)
  • Need: volatile (Java), atomic (Go/C++), Mutex, sync primitives

Q12: When would you use mmap?

Traditional: read(fd, buf, size) → 2 copies (kernel→user)
mmap: ptr = mmap(file) → 0 copies (zero-copy I/O)

Use cases:
 - Database engines (SQLite, LMDB)
 - Reading huge files without loading entirely
 - Shared memory between processes
 - JIT compilers (mmap + PROT_EXEC)

Red Flags (bad answers interviewers watch for)

Bad SignWhy It's Bad"Stack is faster because LIFO"Misses real reason: cache locality"GC means no memory leaks"Wrong — unintentional references still leak"malloc is a system call"Wrong — it's user-space; calls OS only when needs more"Virtual memory = swap"Swap is one feature; isolation matters more"Arrays and linked lists are both O(n)"Ignores 30× cache difference"I use GC language so don't need this"GC doesn't eliminate memory behavior knowledge
#interview (1) #virtual memory (2) #cache (1) #malloc (1) #escape analysis (1) #memory (3) #heap (3) #stack (4)
List