Skip to main content

Memory Without Mystery

How to understand references, lifecycle, mutation, and leaks without turning the topic into a compiler lecture.

Andrews Ribeiro

Andrews Ribeiro

Founder & Engineer

The problem

Memory in JavaScript is often taught as a bag of disconnected words:

  • stack
  • heap
  • garbage collector
  • reference

People walk away knowing the vocabulary but still not being able to answer the questions that matter:

  • Why is this object still alive?
  • Why did changing it here affect some other part of the app?
  • Why does this process keep growing in RAM?

Without a simple model, everything starts to feel like “JavaScript being weird.”

Mental model

You do not need to turn this into a compiler class.

Keep these three ideas:

  • simple values are usually cheap to copy
  • objects, arrays, and functions usually move around by reference
  • memory can only be freed when nothing in the program can still reach that value

Short version:

Memory gets easier when you think in terms of references, reachability, and lifecycle.

Breaking it down

Was it copied or shared?

This is the first filter.

If you do this:

const a = { name: 'Ana' }
const b = a

you did not create two objects.

You created two variables pointing to the same object.

If you forget that, a large part of mutation bugs starts right there.

Who can still reach this value?

A value does not disappear just because you stopped looking at it.

It disappears when the whole program stops being able to reach it.

That is why objects stay alive when they are still held by:

  • a cache
  • a global array
  • a Map
  • a closure
  • an event listener

Should this value still exist right now?

This question is more useful than talking about the garbage collector like it is some mystical cleanup robot.

The real issue is not “did the GC run.”

The real issue is:

  • should this data still be here?
  • does this cache have an eviction rule?
  • did we remove this listener?

A leak is usually accumulation, not black magic

People often imagine a memory leak as some hidden hole inside the runtime.

In practice, the common cases are less dramatic:

  • a list that only grows
  • a cache without expiration
  • a big object trapped in a closure
  • a session map that never removes old entries

So the problem is usually more about lifecycle design than about the engine losing control.

When you investigate a memory or mutation issue, ask:

  1. Was this value copied, or are two variables sharing the same reference?
  2. Who can still access this object?
  3. Should this value still exist right now?
  4. Is some array, cache, closure, or Map growing without cleanup?

Simple example

Look at this common trap:

const user = { name: 'Ana' }
const sameUser = user

sameUser.name = 'Bia'

console.log(user.name)

The output is:

Bia

That did not happen because JavaScript copied the object badly.

user and sameUser point to the same object in memory. When you change sameUser.name, you are also changing user.name.

That detail explains a lot of “mysterious” mutations in:

  • shared state
  • React apps
  • Node services
  • legacy code with too many side effects

Common mistakes

  • assuming that assigning an object to another variable creates a fresh copy
  • forgetting that shared references spread side effects across the app
  • accumulating data in caches, stores, arrays, or Maps without cleanup rules
  • talking about the garbage collector as if it can repair weak architecture on its own

How a senior thinks

More experienced engineers look at memory in terms of ownership and reachability.

The reasoning usually sounds like this:

The problem is not only where this data was created. The problem is who can still reach it and how long we are letting it stay alive.

That change in framing improves debugging fast:

  • leaks stop feeling abstract
  • mutation stops feeling random
  • lifecycle becomes visible

What the interviewer wants to see

In interviews, what usually matters is whether you can connect the theory to a real bug.

  • you understand the difference between a real copy and a shared reference
  • you can explain why a “deleted” value still exists in memory
  • you can tie the concept to a real issue like silent mutation or rising RAM usage

A strong answer often sounds like this:

I think about memory as lifecycle. If some cache, listener, or closure can still reach the value, it is still alive, even if I no longer want it there.

Garbage collection helps clean up what became unreachable. It does not fix an architecture that keeps holding the reference.

Quick summary

What to keep in your head

Practice checklist

Use this when you answer

You finished this article

Next article How to Think About Time and Space Complexity Without Freezing Previous article Node Is Not Single-Threaded in the Way It Seems

Keep exploring

Related articles