메모리 누수 해결해보기 (with. node -inspect)

이명진·2024년 11월 1일
0

내가 사용하는 api 중 메모리가 가득차서 오류가 나는 일이 생겨나게 되었다.

오류 발견

처음에는 메모리를 늘려주는 조건을 주어서 해결하려고 하였으나

다시한번 오류를 만나게 되었는데
이는 지속시간만 늘려주는 일 만 초래했고 근본적인 이유를 찾아야 했다.

확인해보니 메모리 사용율이 계속해서 증가하는 것을 발견하게 되었다.

그래프가 지속적으로 증가하다가 팍 죽는데 (팍 죽는것은 에러가 나서 서버가 셧다운 되버려서 쭉 내려간것)

어떻게 해결해야 좋을지 찾아보다가 메모리 누수에 관련하여서 알게 되었다.

메모리 누수 해결하기

도움이 된 페이지 : https://yozm.wishket.com/magazine/detail/2505/

node js로 만들어 진 코드였기에 명령어를 입력해서 로컬로 확인해보기로 하였다.

명령어로 node -inspect index.js 를 쳐준다.

실행될때 이곳에서 콘솔을 체크할수 있다.사이트 주소에 넣어주자. chrome//inspect/#devices

도움이 된 페이지에서 공부한대로 프로파일링을 녹화하면서 원인을 찾아 보게 되었다.

범인을 찾기 위해 그래프를 분석했다. 파란색 그래프가 문제가 된다는 것이다.

용어 정리 (출처 위의 도움된 페이지 사이트 )

파란색 그래프 : GC가 수거하지 못한 힙 메모리, 할당된 힙 메모리
회색 그래프 : GC가 메모리를 수거함
shallow size 와 Retained Size 를 확인하는 것도 좋다.
Shallow Size : 자신의 크기
Retained Size: 자신 + 참조하고 있는 오브젝트의 크기
Distance : 가비지 콜렉터의 루트로부터 얼마나 떨어져 있는가 (값이 크면 메모리 누수가 일어날 가능성이 높다)

상세하게 코드를 보면서 무엇이 문제일까 찾아보게 되고 코드를 확인해봐도 내문제는 아닌것 같았다.

그래프를 확인해봐도 회색으로 잘 변해가는것 같기도 하고 테스트 횟수를 5번 정도로 돌렸는데 횟수 문제인가? 생각하면서 100번 돌려보기도 했는데
문제가 없었다. 메모리는 22mb로 고정되어 있었다.

Constructor 에서 뭐가 문제인지 쭉 살펴봤다. constructor에서는 어디 파일인지 자세히도 나온다.

확인해보니 nodemodules 의 puppeteer 가 지속적으로 높은 메모리 사용량으로 잡히게 되었다.

문제는 puppeteer ?

puppeteer 자체가 메모리 누수가 이어 져서 검색하게 되었더니

github 에서 이와 관련하여 많은 논의가 있었던것이 보였다.

관련 사이트 : https://github.com/puppeteer/puppeteer/issues/9186 , https://github.com/puppeteer/puppeteer/issues/6414

글을 쭉 읽어봤는데 gregg-cbs 이분이 문제를 제기하고 최근까지 많이 검토한것 같은데 아직까지 해결되지는 않아 보인다.

그럼 결국 puppeteer 말고 다른 것을 써야 하는가?...

해결 방안으로는 두가지가 있었다.

  1. 정기적으로 서버를 내렸다가 다시 리스타트 해주기 (1시간 주기별로 ? )
  2. 다른 대안의 라이브러리를 찾는다.

다시 천천히 점검해보자 😇

다른 대안을 검색해보기도 하고 내 코드로 돌아가서 전역 변수의 문제인지 확인해보기 위해서 쭉 처음부터 코드를 확인했다.

코드를 쭉 보다보니 오류가 날 경우 500에러를 리턴하고 끝나는 것이 보였다. (코드는 보안상 대체했습니다.)

catch (error) {
    console.log("result is 500 error : something went wrong", error);
    await browser.close(); // 추가해줬다.
    browser = null; // 추가해줬다. 
    return res
      .status(500)
      .json({ error: "something went wrong!", errorDetail: error });
  }

기존에는 탭을 키고 끄기만 했었다. 브라우저 자체를 키고 끄는 것이 리소스를 더먹을거라고 판단했기 때문이다.

500에러가 난 이후에 브라우저를 닫는 명령어를 추가해주었고

브라우저를 매번 킬때 조건이 전역 변수 browser가 null일때 브라우저를 키는 함수를 만들어 뒀는데

에러가 발생했을 경우 browser를 null상태로 바꿔주기만 하였다. 이것이 브라우저를 계속 쌓게 해서 메모리를 잡아먹는것이 아닐까 추측하고
코드를 수정하고 재배포를 진행했다.

이후 결과를 기다려 봤다.

효과가 있었던것 같다. 🥰🥰🥰

두시간 정도로 문제가 발생했었던 기존 사례와 다르게 두시간 정도 쭉 테스트 해봤는데 50% 로 지속되는것이 보였다.

기존과의 비교 샷

하루 전만 해도 메모리가 치솟다가 죽어버렸는데 오른쪽을 보면 지속되어 가는것이 보인다

브라우저 가 몇개 열렸는지 로컬에서는 확인할수 있었는데 나는 Google cloud 에 올린것이라서 명령어를 사용할수 없어서
배열로 관리를 해야 하나 라고 고민까지 해봤는데 다행히도 문제를 해결할수 있었다.

내가 진행했던 수정이 유의미한 결과인지는 조금 더 시간을 두고 테스트해봐야 할것 같다.

중요한것은 역시 꺾이지않는 마음과 포기하지않는 마음

이번에 그래도 메모리 누수에 대해서 배우고 node -inspect 에 대해서 공부하며 디버깅 해볼수 있었다.
나중에도 메모리 누수관련하면 또 테스트 해볼수 있을것 같다

결과적으로는 node -inspect로는 해결한 문제는 아니지만 공부할수 있었던 좋은 계기가 된 사건이다

profile
프론트엔드 개발자 초보에서 고수까지!

0개의 댓글