惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Security Affairs
S
Schneier on Security
N
News | PayPal Newsroom
T
Threatpost
Cloudbric
Cloudbric
H
Heimdal Security Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Google Online Security Blog
Google Online Security Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Spread Privacy
Spread Privacy
V
Vulnerabilities – Threatpost
The Last Watchdog
The Last Watchdog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
L
LINUX DO - 最新话题
P
Proofpoint News Feed
C
CXSECURITY Database RSS Feed - CXSecurity.com
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Apple Machine Learning Research
Apple Machine Learning Research
NISL@THU
NISL@THU
Application and Cybersecurity Blog
Application and Cybersecurity Blog
The Hacker News
The Hacker News
O
OpenAI News
人人都是产品经理
人人都是产品经理
C
Cyber Attacks, Cyber Crime and Cyber Security
C
Check Point Blog
C
Cisco Blogs
GbyAI
GbyAI
J
Java Code Geeks
L
LangChain Blog
I
Intezer
T
Tailwind CSS Blog
有赞技术团队
有赞技术团队
MyScale Blog
MyScale Blog
美团技术团队
The Register - Security
The Register - Security
Help Net Security
Help Net Security
WordPress大学
WordPress大学
Y
Y Combinator Blog
T
Tor Project blog
M
MIT News - Artificial intelligence
爱范儿
爱范儿
TaoSecurity Blog
TaoSecurity Blog
V
Visual Studio Blog
T
Threat Research - Cisco Blogs
P
Palo Alto Networks Blog
月光博客
月光博客
T
Tenable Blog
S
Securelist
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
DataBreaches.Net

Random Thoughts

LLM 训练与推理的基本理解 昨日的世界不再重来 Magic Brush - 画出你自己的产品宇宙 写在 AI Coding 奇点之后 尼泊尔布恩山小环线纪行 2025 投资组合年报 三十而笠 我的 8 年职业生涯回顾 我的颈椎病康复之旅 - 关于选择的故事 熊野古道中边路纪行 新加坡旅居三年再回首 高加索三国 - 阿塞拜疆行记 2024 投资组合年报 重新思考 Go:了解程序在线上是如何运行的 欧游散记 —— 环勃朗峰之旅 欧游散记 —— 特摩索斯古城 重新思考 Go:Channel 不是「消息队列」 重新思考 Go:Slice 只是「操作视图」 那一天,我决定踏出一步 True Story 少数价值 A Snapshot of Myself - 2021 RPC 漫谈: 连接问题 RPC 漫谈:序列化问题 RPC 漫谈: 限流问题 科学,技术与工程 Pond: Golang 通用对象池 Golang for-range 内部实现 Golang Interface 内部实现 真理的有限性 Golang rand 库锁竞争优化 Meaningless or Meaningful 分布式文件系统的演化 从动物森友会聊主机游戏联机机制 创业公司的文化 伊朗见闻录 设计实现高性能本地内存缓存 Travel Map Linux I/O 栈浅析 SSD 背后的奥秘 什么是真正的编程能力 沉默与反沉默的理由 命令行里的设计艺术 自由意志下的选择 学习思维方式而非学习观点 从程序到人 —— 情头配对助手的前世今生 即刻多端实时通信实践 朝鲜 —— 小国寡民的主体思想实践 使用 Surge 提升多网络环境下的流畅开发体验 一份其实好吃的 LaTeX 入门餐 Kafka 的设计与实践思考 欧游散记 —— 民主专制下的德国 欧游散记 —— 维也纳 欧游散记 —— 西班牙 欧游散记 —— 德国国会大厦 社会矛盾讨论统一框架的一种可能性 欧游散记 —— 伪君子布拉格 Lemon : Koa 风格的 Python 异步 Web 框架 Golang : Make Programming Happy Again Podcast 闲言碎语 Kubernetes 中使用 API Gateway 替代 Ingress ElasticSearch 最佳实践 blog.joway.io Presentations
NodeJS 内存泄漏检测与定位
Joway · 2019-11-10 · via Random Thoughts

最近解决了一个 Node.JS 应用内存泄漏 Bug,顺便学会了用 Chrome DevTools 去看 heapdump 文件。这里做一些简单的记录。

如何「优雅地」获得 heapdump 文件

由于我们所有应用都是以容器部署的,所以要去获得某个容器内的文件,并拷贝到本地难度还是比较大,也非常麻烦。考虑到调试时或许会需要下载非常多次的 snapshot 文件,建议可以包下 heapdump 库,做成一个接口,把文件 dump 之后再传输给客户端,这样一劳永逸。

需要小心的是,在 heapdump 的时候内存很容易翻倍,所以当内存超过 500 MB的时候如果去 heapdump 非常容易导致 OOM 从而 Crash。

如何检测内存泄漏

检查内存泄漏有两种方法,一种是针对比较大的内存泄漏可以直接观察内存是否一直在稳步上升。如果是一些小的泄漏使得内存上升变化并不非常明显的话,可以通过对比不同时间的 heapdump 文件。

有时候内存上升也可能是因为本身访问量就在上升,所以需要两者对比着分析。

Heapdump 文件对比

通过下载两份间隔一段时间(几分钟)的 heapdump 文件,打开 Chrome DevTools,进入 Memory Tab,选择 Load。选中其中时间更近的 heapdump ,并选择 Comparison,比较对象是老的那份 heapdump:

此时可以选择按 Delta 排序,可以看到两个时间点增加了哪些新的对象。

如图可以看到 string 和 Object 的 Delta 是差不多的,所以可以比较确定是由于 Object 里产生了大量一些 string 对象导致的数量增多,但并不一定能够100%确定是内存泄漏,也可能是正常业务波动。此时需要再拉新的一个时间点的 heapdump 文件再来对比,如果一直在增加,那么内存泄漏的可能性就非常大了。

如何定位内存泄漏

首先依旧是拿到 heapdump 文件,并在 Chrome 中打开。

这里有一些名词需要解释含义:

  • Distance: 距离 GC 跟节点的距离
  • Objects Count: 对象数目
  • Shallow Size: 对象自身被创建时,在堆上申请的大小
  • Retained Size: 把此对象从堆上移除,FullGC 能够释放的空间大小

我们可以先不管别的值,只看 Retained Size 。从上图我们可以看到,Object 的 Retained Size 是最大的,所以可以点开浏览它里面的元素。

如图标红的是该元素的引用关系,即在代码 [engine.io/lib/server.js](http://engine.io/lib/server.js)nsps(Server) 对象的 /notification 属性下的 adapter(Namespace) 属性里的 sids 属性中引用了我们选中的对象 72257。sids 的值就是选中的对象。

通过查看这个对象,我们能够发现是否存在异常的内容,而通过 Retainers 里的引用关系,我们能够找到该对象在代码中的定位。如果值的内容并无异常,那有可能是 Retainers 里的引用关系导致它一直没有被释放。

此处很难总结出什么方法论,但主要思想就是根据 Retained Size 递减排序一路找下去。只要是内存泄漏, Retained Size 一定是会高,但反过来 Retained Size 高不一定是内存泄漏,依照这个逻辑,顺藤摸瓜总能找到一些蛛丝马迹。