목차

반응형

1. 개요

2. 메모리 누수란?

3. 메모리 누수 확인

4. 노드 크롬 디버거

5. 누수 발생 확인

 

 

 

 

수많은 글들이 노드의 메모리 누수에 대한 글들을 설명하고 해결방법, 대안을 포스팅했지만 정말 겉만 번지르르하고 도움이 안 됐다.

사실 좀 화가 났다. 왜냐면 나는 개발을 다 해놓고 앱을 돌려보니까 메모리 누수가 발생했는데 어디서 누수가 발생했는지 감도 안 잡히고 찾아보면 대부분의 게시물이 '메모리 누수는 스택과 힙 영역에서 머시기머시기 -> 정말 간단한 테스트 코드로 메모리 할당된 거 보여주면서 요런 식으로 메모리 누수가 발생하고 이렇게 하면 안 됩니다.~~' 이렇게 끝난다. 뭐 어따쓰라고 ㅡㅡ

그래서 직접 부딪혀보면서 메모리 누수를 찾고 해결해내었다. 결론부터 말하자면 개발을 엄청 잘한다고 메모리 누수를 피해 갈 수 있는 것이 아니다. 왜냐면 정말 많은 사람들이 사용하는 라이브러리에서도 엄청난 메모리 누수가 발생하였기 때문이다.

즉 당신은 메모리 누수가 발생하였으면 직접 덤프를 까보며 분석하고 해결하는 능력을 길러야 한다.

앞으로 포스팅할 글들은 실제 개발하다가 메모리 누수가 발생하였고 누수에 대한 확인, 해결방법을 제시하도록 하겠다.

꽤나 가치 있는 게시물이 될 것이라고 장담한다.

 

2. 메모리 누수란?

메모리 누수에 대한 간단한 설명을 하겠다.

 

당신은 온라인 게임을 만들었다. 한 명의 유저가 접속하면 유저에 대한 객체를 생성하는데 1MB가 필요하다.

 

 

축하한다. 당신이 만든 온라인 게임에 유저 100명이 들어왔다. 100MB의 메모리를 할당받았다.

 

 

안타깝게도 당신의 게임이 너무 빨리 질려버린 탓에 50명의 유저가 종료를 해버렸다.

그래서 50개의 유저 데이터를 파괴하고 50MB의 메모리를 다시 돌려받아야 한다.

GC는 이러한 과정 속에서 사용이 완료되고 다시 사용할 수 있도록 메모리를 수거하고 다시 사용 가능하도록 관리한다.

 

여기서 문제가 발생한다. 정체불명의 이유로 인해서 유저 50명이 접속을 종료해도 GC가 판단하기에 50명의 데이터를 담 고 있던 메모리가 아직 사용이 완료됐다고 판단하지 않아서 메모리를 수거하지 않고 계속 남아있는 것이다.

 

 

이것이 계속 반복되면 결국 앱은 무한대로 메모리를 점유하게 될 것이고 언젠가 앱이 강제로 종료될 것이다.

 

메모리 할당 -> 사용 끝 -> GC가 수거 못함 -> 아무도 사용 안 하지만 계속 할당된 채로 남아있음 -> 반복 -> 서버 폭발

 

3. 메모리 누수 확인

그러면 실제 개발하다가 메모리 누수가 발생하면 어떻게 되는지 확인해보자

일단 먼저 본인의 앱에서 메모리 누수가 발생한다는 것을 인지해야 한다.

왜냐하면 당신의 앱에서도 충분히 메모리 누수가 발생하고 있지만 그 정도가 

나는 메모리 누수에 대한 개념이 부족한 때라서 메모리 누수를 인지하는데 상당히 오래 걸렸다.

 

작업 관리자에서 Node가 차지하는 메모리를 보면 2000mb인 것을 볼 수 있다. 말도 안 되는 상황이지 않는가?

 

 

4. 노드 크롬 디버거

먼저 노드의 메모리 누수를 확인하기 위해서는 노드 크롬 디버거를 사용해야 한다.

노드 크롬 디버거를 사용하는 방법은 다음과 같다.

 

node --inspect 시작 파일

 

그러면 위와 같은 메시지가 추가로 나온다. 포트가 9229여야 하는데 꼭 9229일 필요는 없지만 구글 크롬에서 기본값으로 부착되는 포트가 9229라서 편하다

 

구글 크롬으로 들어간다.

 

URL에 chrome://inspect로 접근한다.

 

위와 같이 하단에 구동 중인 서버가 보인다. inspect를 눌러서 디버거를 켠다.

 

Memory > Profiles의 메모리 부분을 보면 현재 애플리케이션이 사용 중인 메모리를 실시간으로 볼 수 있다.

저 상태에서 메모리 부하를 일으키는 작업을 실행해보자 메모리가 하늘 높은 줄 모르고 계속 치솟는다.

 

그러다가 앱이 멈추면서 특정 부분에서 break를 한다. 우측을 보면 Paused before potential out-of-memory crash라고 나와있다. 메모리 부하가 심해서 임종 직전에 멈춘 것이라고 한다.

 

 

5. 누수 발생 확인

메모리 누수가 발생하는 원인을 확인해보는 여러 가지 방법을 설명하도록 하겠다.

 

메모리 스냅샷을 이용하여 부하를 일으키는 메모리는 어떤 종류인지 확인해보겠다.

메모리 부하를 일으킨 다음에 메모리 스냅을 떠서 용량이 많이 차지하는 덩어리를 한번 까보는 것이다.

 

일단 아무것도 하지 않은 위 상태에서 스냅샷을 한번 찍는다.

 

 

이전 단계와 마찬가지로 부하를 일으켜보고 어느 정도 됐다 싶으면 스냅을 한번 더 찍는다.

 

두 개의 스냅이 저장됐다.

 

각 스냅의 Statics를 확인해보자.

 

좌측은 정상적인 어플리케이션 메모리의 분포 상황이고 우측은 딱봐도 이상하리만큼 Strings로 꽉찬 메모리 분포 상황이다. 이정도면 엄청나게 많은 string을 만들어놓고 gc 처리를 못해서 메모리 부하가 생긴다고 킹리적 갓심이 가능한 상황이다. ㅇㅈ?ㅇㅈ

 

 

이번에는 저 많은 string이 어떤 부분에서 발생하는지 확인해보도록 하자.

 

앱을 다시 시작하고 부하를 주지 않은 상태에서 Allocation sampling 을 선택하고 Start를 누르고 어느정도 부하가 되면 Stop을 눌러서 결과를 살펴보자.

Allocation sampling은 메모리 부하를 주는 자바스크립트의 메쏘드를 살펴보는 기능이다.

 

Tree형식으로 확인해보자

 

혼자서 무려 메모리 사이즈의 99.99%를 차지하는 불순한 녀석이 보인다. 저 녀석의 하위 트리를 따라내려가보면서 쭈욱 살펴보도록 하자

 

jsonparse라는 내가 설치한 라이브러리가 해당 문제를 일으키고 있으며 해당 라이브러리의 문제를 일으키는 메쏘드들을 확인할 수 있다.

 

그러면 마지막으로 메모리 누수가 발생하는데 어떤 형태의 데이터가 쌓이는지 그 값들을 직접 확인해보자

 

다시한번더 어플리케이션의 초기상태, 부하상태의 스냅샷을 각각 찍어보자

 

All Obejcts를 두 스냅샷 사이이로 해주고 Summary를 Comparison으로 해주자

 

 

그러면 두 스냅샷 사이에서 생성된 데이터를 볼 수 있다.

 

Alloc.Size로 내림차순 정렬을 하고 상단의 트리를 열어보자

 

그러면 어 저거 내가 생성한 데이터인데 왜 저기있지? 싶은것이 있으면 원인을 찾은 것이다.

분명 저 데이터는 처리되고 말소되어서 GC가 수거해가야하는데 수거하지 못하게끔 어딘가에 계속 맞물려있는 것이다.

이렇게 해서 원인을 찾아봤고 다음은 해결하는 방법에 대해서 작성해보겠다.

 

 

Nodejs - 메모리 JavaScript heap out of memory

Nodejs - 메모리 할당 사이즈 변경

Nodejs - 메모리 누수 확인

반응형