M4RKYU.SYSEdition 2027
Skip to content
LOCZH/安大略 · 加拿大/▸logs · in depth analysis of javascript memory model and lifecycle 2o55待机OK/--:--:--EST
M4M4RK_YUportfolio
  • 创作创作
    创作Overview
    • 作品精选案例与项目记录
    • 游戏可玩原型与游戏开发日志
  • 影像影像
    影像Overview
    • 照片影像合集与视觉实验
    • 商店印刷品、海报和限量物件
  • 写作写作
    写作Overview
    • 博客长篇开发日志与现场笔记
    • 笔记短观察、链接与代码片段
  • 资源资源
    资源Overview
    • 工具38 款浏览器内开发工具
    • 链接每日使用的开发与设计书签
  • 关于关于
  • 联系联系
EN

同步 · dev.to / @markyu

JavaScript Memory Leaks Usually Start With One Reference

A practical JavaScript memory guide covering stack, heap, garbage collection, closures, event listeners, timers, and leak debugging.

发布日期
May 22 '24
·
阅读时长
2 min read
·
点赞
6
javascriptperformancewebdevdebugging
在 dev.to 查看

本页目录

  • Stack vs Heap
  • The Leak Pattern
  • Event Listener Leak
  • Timer Leak
  • Closure Surprise
  • Debugging Checklist
  • Why This Still Matters in 2026
  • Final Thought

Most JavaScript memory leaks I have debugged were not dramatic.

They were one forgotten event listener, one timer that never stopped, or one closure holding a reference longer than expected.

The browser cannot collect memory if your code still points at it.

That is the whole game.

Stack vs Heap

The simplified model:

stack -> function calls, local primitives, references
heap  -> objects, arrays, functions, DOM nodes

Example:

function createUser() {
  const name = "Mark";
  const user = { name, role: "admin" };
  return user;
}

name is simple. user is an object on the heap, and the variable holds a reference to it.

When no live reference can reach that object, garbage collection can reclaim it.

The Leak Pattern

const cache = [];

function rememberUser(user) {
  cache.push(user);
}

If cache grows forever, those users stay reachable forever.

Visual map:

global cache -> user object -> nested profile -> big data

The garbage collector is not failing. Your references are doing exactly what you asked.

Event Listener Leak

This is common in UI code:

function mount() {
  window.addEventListener("resize", handleResize);
}

If you mount/unmount repeatedly and never remove the listener, old handlers stay alive.

Better:

function mount() {
  window.addEventListener("resize", handleResize);

  return () => {
    window.removeEventListener("resize", handleResize);
  };
}

React version:

useEffect(() => {
  window.addEventListener("resize", handleResize);
  return () => window.removeEventListener("resize", handleResize);
}, []);

Timer Leak

setInterval(() => {
  refreshDashboard();
}, 5000);

If the page or component is gone but the interval is still running, you have a leak or at least wasted work.

const intervalId = setInterval(refreshDashboard, 5000);
clearInterval(intervalId);

In React:

useEffect(() => {
  const id = setInterval(refreshDashboard, 5000);
  return () => clearInterval(id);
}, []);

Closure Surprise

Closures are useful, but they can keep data alive.

function createHandler(bigPayload) {
  return function onClick() {
    console.log(bigPayload.id);
  };
}

If onClick is stored somewhere long-lived, bigPayload stays alive too.

That is not a bug in closures. It is how closures work.

Debugging Checklist

In Chrome DevTools:

  1. Open Memory panel.
  2. Take a heap snapshot.
  3. Interact with the page.
  4. Force garbage collection if appropriate.
  5. Take another snapshot.
  6. Compare retained objects.

Look for:

  • detached DOM nodes
  • growing arrays/maps
  • duplicate listeners
  • long-lived closures
  • cached API responses with no eviction

Why This Still Matters in 2026

Modern frontends are heavier now: dashboards, AI chat UIs, long-context document tools, canvas apps, and multimodal assistants can all keep large objects around.

If your app stores every token, message, image preview, and intermediate state forever, the browser will eventually punish you.

Final Thought

JavaScript memory management is automatic, not magical.

The garbage collector cleans unreachable objects. Your job is to stop accidentally keeping everything reachable.

What was the weirdest memory leak you have debugged in JavaScript?

相关阅读

Next.js Images Without CLS: My LQIP Blur-Up Setup

A practical Next.js image optimization guide for zero CLS layouts, blur placeholders, dimensions, remote images, and production image hygiene.

nextjs

Frontend Linear Data Structures Deep Dive: Arrays, Stacks, Queues, and Linked Lists

The Big Picture Before diving into stacks, queues, and linked lists, it helps to know...

computerscience

JS Sorting Algorithms Every Developer Should Know

You call .sort() every day without thinking about it. But the sorting algorithm your language's...

algorithms

原文发布

本文首发于 dev.to,评论与点赞保留在原站。

在 dev.to 继续阅读
上一篇CSS Heart Animation: Small Demo, Real Animation LessonsA cleaner CSS heart animation tutorial focused on transform, pseudo-elements, keyframes, and the small mistakes that break simple UI animations.
返回全部文章
下一篇Java throw vs throws: The Exception Bug They RevealA practical Java exception handling guide explaining throw, throws, checked exceptions, runtime exceptions, and API boundary decisions.
返回档案
M4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYU
始于 2024
ZhenXiao Mark YuZhenXiao Mark Yu
联系

看到什么有意思的?和我聊聊。

这是一个作品集,不是服务 · 但每一条留言我都会看 — 如果哪里让你有所触动,或者只想打个招呼,欢迎写信过来。

开启对话
频道开放

随时打个招呼 · 2026

--:--:--EST加拿大 安大略
  • 邮件
  • GitHub
  • dev.to
  • 领英
  • 推特 / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat

订阅

偶尔收到一封简讯

来自 m4rkyu.com 的笔记与日志——简短、标注日期、没有杂音。随时可退订。

作品

线上发布、游戏作品与视觉档案。

  • 项目
  • 游戏
  • 档案
  • 日志

资源

每日好用的工具与个人收藏的链接库。

  • 搜索
  • 最新
  • 工具
  • 链接
  • 笔记
  • 主题
  • 商店
RSSJSON Feed

工作室

背景、联系方式以及合作渠道。

  • 关于
  • 联系
  • 更新日志
  • 技术说明
  • 简历筹备中

社交

在常去的平台上找到我。

  • GitHub
  • dev.to
  • 领英
  • 推特 / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat
  • 邮件
© 2026 ZhenXiao Mark Yumarkyu0615@gmail.com
  • 邮件
  • GitHub
  • dev.to
  • 领英
  • 推特 / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat
隐私条款由 Next.js 16 · React 19 · Tailwind 4 构建