노드에서 메모리 누수 문제를 해결하는 방법은 무엇입니까? 다음 문서는 모든 사람에게 도움이 될 수 있는 노드 메모리 누수 문제 해결 경험을 요약합니다.
in Nodejs
服务端开发的场景中,内存泄漏
은 확실히 가장 짜증나는 문제입니다.
그러나 프로젝트가 계속해서 개발되고 반복되는 한 엔지니어의 가장 기본적이고 핵심적인 역량이 나타날 것입니다. 内存泄漏
的问题绝对不可避免,只是出现的时间早晚而已。所以系统性掌握有效的 内存泄漏
排查方法是一名Nodejs
메모리 누수
를 해결한 사례를 바탕으로 처리 아이디어를 공유하겠습니다.
内存泄漏
的案例分享一下我的处理思路。2022 Q4
2022년 4분기
어느 날 R&D 사용자 그룹에서 우리 R&D 플랫폼을 사용할 수 없다고 보고했습니다. 액세스했지만 다수의 비정상적인 작업이 백그라운드에서 완료되지 않았습니다.
첫 번째 반응은 메모리 누수가 있을 수 있다는 것입니다. 다행히 grafana
에서 모니터링(prometheus
+ grafana
)으로 서비스가 연결되어 있습니다. 모니터링 패널을 살펴보니 10시 이후에는 메모리가 계속 증가하고 한 번도 다운되지 않았으며 명백한 데이터 유출이 있는 것으로 나타났습니다. [추천 관련 튜토리얼: nodejs 동영상 튜토리얼]prometheus
+ grafana
),在grafana
监控面板中发现在 10.00 后内存一直在涨没有下来过出现了明显的数据泄漏。【相关教程推荐:nodejs视频教程】
说明:
process memory
:rss
(Resident Set Size),进程的常驻内存大小。heapTotal
: V8 堆的总大小。heapUsed
: V8 堆已使用的大小。external
: V8 堆外的内存使用量。在
Nodejs
中可以调用全局方法process.memoryUsage()
获取这些数据其中heapTotal
和heapUsed
是 V8 堆的使用情况,V8 堆是Node.js
中 JavaScript 对象存储的地方。而external
则表示非 V8 堆中分配的内存,例如 C++ 对象。rss
则是进程所有内存的使用量。一般看监控数据的时候重点关注heapUsed
的指标就行了
内存泄漏主要分为:
其实不管是全局性内存泄漏还是局部性的内存泄漏,要做的都是尽可能缩小排除范围。
全局性内容泄漏出现一般高发于:中间件
与组件
中,这种类型的内存泄漏排查起来也是最简单的。
很遗憾我在 2022 Q4
中遇到的内存泄漏不属于这个类型,所以还得按照局部性泄漏的思路进行分析。
这种类型我就不讲其它科学的分析方法了,这种情况下我认为使用二分法排查是最快的。
流程流程:
先注释一半的代码(减少一半中间件
、组件
、或其它公用逻辑的使用)
随便选择一个接口或新写一个测试接口进行压测
如果出现内存泄漏,那么泄漏点就在当前使用的代码之中,若没有泄漏则泄漏点出现在
然后一直循环往复上述流程大约 20 ~ 60 min 一定可以定位到内存泄漏的详细位置
2020 年的时候我在做基于
Nuxt
SSR 应用时,上线前压测发现应用内存泄漏,判断定为全局性的泄漏之后,采用二分法排查大约花了 30min 就成功定位了问题。
当时泄漏的原因是我们在服务端使用axios
导致的泄漏,后来统一axios
相关的全换成node-fetch
后就解决了,从此换上了axios PDST
后来绝对不会在Node
服务中使用axios
了
大多数内存泄漏的情况都是局部性的泄漏,泄漏点可能存在与某个中间件
、某个接口
、某个异步任务
中,由于这样的特性它的排查难度也较大。这种情况都会做 heapdump
🎜설명: 🎜🎜
프로세스 메모리
:rss
(상주 세트 크기), 프로세스의 상주 메모리 크기입니다.heapTotal
: V8 힙의 전체 크기입니다.heapUsed
: 사용된 V8 힙의 크기입니다.외부
: V8 외부 힙 메모리 사용량.Nodejs
에서 전역 메서드process.memoryUsage()
를 호출하여 이러한 데이터를 얻을 수 있습니다. 그중heapTotal
및heapUsed
는Node.js
의 JavaScript 객체가 저장되는 V8 힙의 사용법입니다. 그리고external
은 C++ 객체와 같이 V8이 아닌 힙에 할당된 메모리를 나타냅니다.rss
는 프로세스의 총 메모리 사용량입니다. 일반적으로 모니터링 데이터를 볼 때heapUsed
표시기🎜
🎜사실 전역 메모리 누수인지 아닌지로 나누어집니다. 또는 로컬 누수 메모리 누수의 경우 제외 범위를 최대한 좁히면 됩니다. 🎜
미들웨어
구성요소
, 이러한 유형의 메모리 누수 역시 문제 해결이 가장 쉽습니다. 🎜🎜안타깝게도 제가 2022 Q4
에서 겪은 메모리 누수는 이 유형에 속하지 않기 때문에 로컬 누수라는 개념에 따라 분석해야 합니다. 🎜미들웨어
절반 줄이기) >, 컴포넌트
또는 기타 공개 로직 사용)🎜🎜2020년로컬 메모리 누수 문제 해결Nuxt
기반 SSR 애플리케이션 작업을 하던 중, 가기 전에 스트레스 테스트를 했는데요. 온라인에서 애플리케이션 메모리 누수를 발견했는데, 이는 글로벌 누수로 판단됐다. 이후 이분법을 사용해 문제를 찾는 데 약 30분 정도 걸렸다.
당시 유출이 발생한 이유는 서버 측에서axios
를 사용했기 때문이었습니다. 나중에axios
를 통합하여node- fetch
그 후에 해결되었고, 그 이후로는node
에서axios
를 사용하지 않을 것입니다. 더 이상 서비스하지 마세요🎜
미들웨어
, 특정 인터페이스
및 특정 비동기 작업
은 이러한 특성으로 인해 문제를 해결하기가 더 어렵습니다. 이 경우 분석을 위해 heapdump
가 수행됩니다. 🎜여기서는 이 경우에 대한 내 아이디어를 주로 다음 단락인 heapdump
的详细说明我放在下个段落,
Heap Dump
:堆转储, 后面部分都使用heapdump
表示,做heapdump
的工具和教程也非常多比如:chrome、vscode、heapdump 这个开源库。我用的 heapdump 库做的网上教程非常多这里不展开了。
局部性内存泄漏排查需要一定的内存泄漏排查经验,每次遇到都把它当成对自己的一次磨砺,这样的经验积累多了以后排查内存泄漏问题会越来越快。
这一点非常重要,明确了这一点可以大幅度缩小排查范围。
经常会出现这种情况,这个迭代做了A、B、C 三个功能,压测时或上线后出现了内存泄漏。那么就可以直接锁定,内存泄漏发生小这三个新的功能之中。这种情况下就不需要非常麻烦的去生产做 heapdump
我们在本地通过一些工具就可以很轻松的分析定位出内存泄漏点。
由于我们 20年Q4
Heap Dump
에 설명하겠습니다. 다음 부분은 모두heapdump
를 사용한다는 것은 Chrome, vscode 및 heapdump 오픈 소스 라이브러리와 같이heapdump
를 수행하기 위한 많은 도구와 튜토리얼이 있다는 것을 의미합니다. 내가 사용하는 힙 덤프 라이브러리에 대한 온라인 자습서가 많이 있지만 여기서는 다루지 않겠습니다.이 더 빠릅니다.
heapdump
를 수행하기 위해 프로덕션으로 이동할 필요가 없습니다. 일부 도구를 통해 로컬에서 메모리 누수 지점을 쉽게 분석하고 찾을 수 있습니다. node
添加 --expose-gc
,这个参数会向全局注入 gc()
方法,方便手动触发 GC 获取更准确的堆快照
数据heapdump
采集堆快照数据时需要特别注意的一些点!
- 在
heapdump
时 Node 服务会中断,根据当时服务器内存大小这个时间会在 2 ~ 30min 左右。在生产环境做heapdump
需要和运维一起制定合理的策略。我在这里是使用了主、备两个pod
, 当主pod
停掉之后,业务请求会通过负载均衡到备用pod
由此保障生产业务的正常进行。(这个过程必定是一个与运维密切配合的过程,毕竟heapdump
玩抽还需要通过他们拿到服务器中堆快照
文件)- 上述接近临界点打印快照只是一个模糊的描述,如果你试过就知道等非常接近临界点再打印内存快照就打印不出来了。所以接近这个度需要自己把握。
- 做至少 3 次
heapdump
(实际上为了拿到最详细的数据我是做了 5 次)
需要你的应用服务接入监控,我这里应用是使用prometheus
+ grafana
做的监控, 主要监控服务的以下指标
QPS
(每秒请求访问量) ,请求状态,及其访问路径ART
(平均接口响应时间) 及其访问数据NodeJs
版本Actice Handlers
(句柄)Event Loop Lag
(事件滞后)rss
、heapTotal
、heapUsed
、external
、heapAvailableDetail
只有
heapdump
数据是不够的,heapdump
数据非常晦涩,就算在可视化工具的加持下也难以准确定位问题。这个时候我是结合了grafana
的一些数据一起看。
由于当时的对快照数据丢失了,我这里模拟一下当时的场景。
1、通过 grafana
监控面看看到内存一直在涨一直下不来,但同时我也注意到,服务中的句柄
2020년 4분기
의 특수한 상황으로 인해 메모리 누수가 처음 발견된 시기는 대략적으로 1월에만 확인할 수 있었습니다. 이번 달에 우리는 또 다른 주요 버전 반복을 거쳤습니다. 이러한 기능과 인터페이스를 하나씩 확인하면 비용이 매우 높을 것입니다.
따라서 추가 분석을 위해서는 더 많은 데이터를 결합해야 합니다2. 힙 덤프 데이터 수집🎜🎜🎜🎜생산이 시작되면 node
는 --expose-gc
를 추가합니다. 매개변수 gc()
메서드는 보다 정확한 힙 스냅샷
데이터를 얻기 위해 GC의 수동 트리거를 용이하게 하기 위해 전역적으로 주입됩니다.🎜여기서 두 개의 인터페이스를 추가하고 가져왔습니다. 독점 권한 보유, 🎜🎜 GC 수동 실행🎜힙 스냅샷 인쇄🎜heapdump
🎜🎜프로젝트 후 처음으로 스냅샷 데이터 인쇄 시작됩니다 🎜메모리가 100M 증가한 후: 먼저 GC를 트리거한 다음 두 번째로 힙 스냅샷 데이터를 인쇄합니다.🎜메모리가 임계 수준에 가까워지면 GC를 다시 트리거한 다음 인쇄합니다. 힙 스냅샷 🎜🎜힙 스냅샷 데이터 수집 시 특별한 주의가 필요한 몇 가지 사항! 🎜🎜🎜🎜heapdump
중에 Node 서비스가 중단됩니다. 이 시간은 해당 시간의 서버 메모리 크기에 따라 약 2~30분 정도 소요됩니다. 프로덕션 환경에서heapdump
를 수행할 때는 운영 및 유지 관리와 협력하여 합리적인 전략을 개발해야 합니다. 여기서는 기본 및 백업이라는 두 개의pod
를 사용합니다. 기본pod
가 중지되면 비즈니스 요청이 백업pod
로 로드 밸런싱됩니다. . 이는 생산 작업의 정상적인 수행을 보장합니다. (이 프로세스는 운영 및 유지 관리와 긴밀하게 조화된 프로세스여야 합니다. 결국heapdump
는 이를 통해 서버에heap snapshot
파일을 가져와야 합니다)🎜 위의 임계점에 가까운 스냅샷을 인쇄하는 것은 막연한 설명일 뿐입니다. 사용해보신 분이라면 임계점에 매우 가깝게 기다렸다가 메모리 스냅샷을 인쇄하면 인쇄되지 않는다는 것을 아실 것입니다. 그러므로 이 정도에 가까워지려면 자신을 통제해야 합니다. 🎜heapdump
를 3번 이상 수행하세요(실제로 가장 자세한 데이터를 얻기 위해 5번 수행했습니다)
prometheus
+ grafana를 사용합니다. 코드별 모니터링>은 주로 서비스의 다음 지표 🎜🎜🎜<code>QPS
(초당 요청 방문 수), 요청 상태 및 액세스 경로🎜ART
)를 모니터링합니다. > (평균 인터페이스 응답 시간) 및 해당 액세스 데이터🎜NodeJs
버전🎜Actice 핸들러
(handle)🎜 이벤트 루프 지연
(이벤트 지연)🎜서비스 프로세스 다시 시작 횟수🎜CPU 사용량🎜메모리 사용량: rss
, heapTotal, <code>heapUsed
, external
, heapAvailableDetail
🎜힙 덤프
만 해당 > 데이터가 충분하지 않고heapdump
데이터가 매우 모호하며 시각화 도구를 지원하더라도 문제를 정확하게 찾기가 어렵습니다. 이때grafana
의 일부 데이터와 함께 살펴보았습니다. 🎜
grafana
모니터링 인터페이스를 통해 메모리가 증가하고 감소하지 않는 것을 확인했습니다. 그러나 동시에 핸들
의 개수도 확인되었습니다. code> 서비스도 급등하며 하락세를 보이지 않고 있습니다. 🎜🎜🎜🎜2. 누수가 발생한 달의 새로운 기능을 검토한 때입니다. bull
메시지 대기열 구성요소 사용으로 인해 메모리 누수가 발생했을 수 있다고 의심됩니다. 먼저 관련 애플리케이션 코드를 분석해 보았는데, 메모리 누수를 일으키는 뭔가 문제가 있다는 것을 알 수 없었습니다.
1의 핸들 누수 문제와 결합하면 bull
后需要手动的去释放某些资源,在这个时候还不太确定具体原因。
3、然后对 5 次的 heapdunmp
数据进行了分析,数据导入 chrome
对 5 次堆快照进行对比后,发现每次创建队列后 TCP、Socket、EventEmitter 的事件都没有被释放到。到这里基本可以确定是由于对 bull
的使用不规范导致的。在 bull
通常不会频繁创建队列,队列占用的系统资源并不会被自动释放,若有需要,需手动释放。
4、在调整完代码后重新进行了压测,问题解决。
Tips: Nodejs 中的
句柄
是一种指针,指向底层系统资源(如文件、网络连接等)。句柄允许 Node.js 程序访问和操作这些资源,而无需直接与底层系统交互。句柄可以是整数或对象,具体取决于 Node.js 库或模块使用的句柄类型。常见句柄
:
fs.open()
返回的文件句柄net.createServer()
返回的网络服务器句柄dgram.createSocket()
返回的 UDP socket 句柄child_process.spawn()
返回的子进程句柄crypto.createHash()
返回的哈希句柄zlib.createGzip()
返回的压缩句柄
通常很多人第一次拿到堆快照
数据是懵的,我也是。在看了网上无数的分析技巧结合自身实战后总结了一些比较好用的技巧,一些基础的使用教程这里就不讲了。这里主要讲数据导入 chrome
를 사용한 후 특정 리소스를 수동으로 해제해야 하는 것 같습니다. 현재로서는 구체적인 이유를 잘 모르겠습니다.
heapdunmp
데이터를 분석하고 해당 데이터를 chrome
으로 가져온 후 5번의 힙 스냅샷을 비교한 결과 다음과 같은 사실을 발견했습니다. 각 큐 생성, TCP, Socket, EventEmitter 이벤트는 해제되지 않습니다. 현시점에서는 기본적으로 의 불규칙한 사용으로 인해 발생하는 것이 확실합니다. 대기열은 일반적으로 에서 자주 생성되지 않으며 대기열이 차지하는 시스템 리소스는 자동으로 해제되지 않습니다. 필요한 경우 수동으로 해제해야 합니다. - 팁: Nodejs의
handle
은 기본 시스템 리소스(예: 파일, 네트워크 연결 등)를 가리키는 포인터입니다. 핸들을 사용하면 Node.js 프로그램이 기본 시스템과 직접 상호 작용하지 않고도 이러한 리소스에 액세스하고 조작할 수 있습니다. 핸들은 Node.js 라이브러리 또는 모듈에서 사용하는 핸들 유형에 따라 정수 또는 객체가 될 수 있습니다. 일반핸들
:TCP
Socket
EventEmitter
global
如果通过 Summary
视图, 不能定位到问题这时我们一般会使用 Comparison
fs.open()
반환된 파일 핸들Comparison
视图中选择两个堆快照,并在它们之间进行比较。您可以查看哪些对象在两个堆快照之间新增,哪些对象在两个堆快照之间减少,以及哪些对象的大小发生了变化。Comparison
net.createServer()
반환된 네트워크 서버 핸들
dgram.createSocket()
반환된 UDP 소켓 핸들child_process.spawn()
반환된 하위 프로세스 핸들 crypto.createHash()
반환된 해시 핸들
zlib.createGzip()
반환된 압축 핸들 힙 스냅샷
데이터를 처음 받으면 혼란스러워하는데, 나도 그렇습니다. 인터넷에서 수많은 분석 기술을 읽고 이를 내 실제 실습과 결합한 후 몇 가지 유용한 기술을 요약했습니다. 일부 기본 사용법 튜토리얼은 여기서 논의하지 않겠습니다. 여기서는 데이터를 chrome
으로 가져온 후 사진을 보는 방법에 대해 주로 설명합니다.
요약 보기
TCP
Socket
EventEmitter
글로벌
요약
을 통과한 경우 보기, 문제를 찾을 수 없는 경우 일반적으로 비교
보기를 사용합니다. 이 보기를 통해 두 힙 스냅샷의 개체 수와 개체가 차지하는 메모리의 변화를 비교할 수 있습니다.
이 정보를 통해 우리는 힙에 있는 개체의 값을 판단할 수 있으며 일정 시간(특정 작업) 이후 메모리 변경 사항을 통해 일부 비정상적인 개체를 찾을 수 있습니다. 이러한 개체의 이름 속성이나 기능은 메모리 누수 조사 범위를 좁힐 수 있습니다. 🎜🎜 🎜 보기에서는 개체 간의 관계뿐 아니라 유형, 크기, 참조 횟수와 같은 개체 세부정보도 볼 수 있습니다. 이 정보를 사용하면 어떤 개체가 메모리 누수를 일으키는지 이해할 수 있습니다. 🎜🎜🎜🎜🎜🎜Containment view🎜🎜🎜에는 개체 간에 연결할 수 있는 모든 참조 관계가 표시됩니다. 각 개체는 점으로 표시되며 선으로 상위 개체와 연결됩니다. 이렇게 하면 개체 간의 계층적 관계를 확인하고 어떤 개체가 메모리 누수를 일으키는지 이해할 수 있습니다. 🎜🎜🎜🎜🎜🎜통계 보기🎜🎜🎜이 사진은 매우 간단하므로 자세히 설명하지 않겠습니다🎜🎜🎜🎜lru-cache
와 같은 메모리 집약적인 타사 라이브러리를 사용하면 메모리 오류가 발생할 수 있습니다. lru-cache
存的太多就会导致内存不够用,在 Nodejs 服务中建议使用 redis
替代 lru-cache
服务需要接入监控,方便第一时间确定问题类型
判断内存泄漏是全局性的还是局部性的
全局性内存泄漏使用二分法快速排查定位
局部内存泄漏
遇到内存泄漏的问题不要畏惧,多积累内存泄漏问题的排查经验处理经验多了找起来就非常快了。每次解决之后做复盘总结回头再多看看
누수 처리: 호출 후 시스템 리소스가 해제되지 않습니다.堆快照
🎜메모리 누수 문제를 겪지 마세요. 메모리 누수 문제 해결에 대한 더 많은 경험을 쌓을수록 더 쉬워집니다. 그들을 찾으려면. 각 솔루션을 마친 후 검토 요약을 수행하고 힙 스냅샷
을 다시 살펴보세요. 데이터는 관련 경험을 더 빠르게 축적하는 데 도움이 됩니다🎜🎜🎜🎜Others🎜🎜🎜🎜스트레스 테스트 도구: wrk🎜🎜🎜더 많은 노드 관련 지식을 보려면 🎜nodejs 튜토리얼🎜을 방문하세요! 🎜
위 내용은 [경험요약] Node에서 메모리 누수 문제를 해결하는 방법은 무엇인가요? 아이디어 공유의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!