저번글에서 mdn에서 설명한 자바스크립트 메모리관리에 대한 내용을 ㅈ간략하게 적어봤는데, GC에 대해서 설명이 좀 부실해서 참고할만한 링크를 다시 가지고 왔다.
개인적으로 GC를 그닥 좋아하지는 않는데, 이전에 일을하면서 경험했던것들 때문인거같다.
원하는 시점에서 trigger할수도 없었고 그렇다고 reclaim하는 free memory의 양이 많았던것도 아니고 동시에 cpu resoucre까지 점유해 정작 boost받아야할 process들의 cpu할당을 지연시켜주는 고마운 존재였지...
여튼 그건 그거고 이건 이거니까~
여튼, GC에 대해서 짧게 정리하고, 자바스크립트에서의 메모리릭에 대해서도 한번 살펴보려고 한다.
한국말로 쓰레기수집... Gargage Collection은 memory management 기법 중 하나로, 프로그램이 동적으로 할당했던 memory 영역 중에서 필요없게 된 영역을 해제하는 기능이다.
(1959년 무렵 개발되었다고 한다...대단하다)
GC는 동적 할당된 메모리 영역 중 더이상 사용할 수 없게된 영역을 탐색해 자동으로 해제하는 기법이다.
여기서 말하는 더 이상 사용할 수 없게된 영역이란, 어떤 변수도 가리키지 않게된 영역을 의미한다.
GC가 지원되는 환경에서는 프로그래머가 동적으로 할당한 메모리 영역의 전체를 완벽하게 관리할 필요가 없어진다. (편하긴함 솔직히)
따라서 아래와 같은 버그를 막거나 줄일 수 있다.
Memory Leak은 부주의 또는 일부 프로그램의 오류로 인해서 더이상 사용하지 않는 메모리를 해제하지 못하는 것을 말한다.
간단하게 말해서 어떤 변수가 100MB의 메모리를 점유한다고 할때, 이 변수가 더 이상 사용되지 않더라도 수동으로 또는 자동으로 해제되지 않아 계속 메모리를 점유하는것을 말한다.
service를 위해 memory resident하는게 아니라, 더 이상 필요하지 않는데도 불구하고 free되지 않는 memory를 말한다.
자바스크립트에서 메모리는 단순 변수에 사용되는 스택 메모리와 복잡한 객체에 사용되는 힙 메모리로 구분된다.
String
, Number
, Boolean
, Null
, Undefined
, Symbol
, Bigint
등이 있다.Object
, Array
, Function
등이 있다.위에서 말한 메모리 릭의 정의에 따르면, 변수 또는 데이터가 더이상 필요하지 않을때 이것들은 GC에 의해서 컬렉팅이 되고 데이터가 정리되면서 free될것이다.
자바스크립트는 자동 정리 매커니즘을 사용하기 때문에, 프로그래머가 얼마나 많은 메모리를 할당하고 비우든지 간에 신경이 쓸 필요가 없다.
하지만 그렇다고 메모리 관리에 신경쓸 필요가 없다는 의미는 아니다.
(진짜 신경쓸 필요가 없다면 memory leak이라는건 js로 개발해서는 발생되지 않겠지...)
다음으로 자바스크립트 GC 메커니즘을 살펴보자.
(일반적으로 전역변수는 자동으로 정리되지 않는다)
function fn1 () {
let a = {
name: 'bytefish'
}
let b = 3
function fn2() {
let c = [1, 2, 3]
}
fn2()
return a
}
let res = fn1()
위 코드의 call stack은 아래와 같다.
image source
그림의 왼쪽 부분은 스택 영역으로 실행 컨텍스트와 원시 타입의 데이터를 저장하는데 사용하고, 오른쪽은 힙 영역으로 객체를 저장하는데 사용된다.
fn2()
가 실행될때, call stack 내부 실행 context는 top to bottom으로 다음과 같이 fn2() => fn1() => 전역 실행 컨텍스트
순서로 존재한다.
따라서 fn2()
가 실행을 완료하면, current context 화살표가 아래로 내려가면서 stact memory공간이 free된다.
또한 이 이후에 함수 fn1()
이 실행이 완료된 후 fn1실행 컨텍스트가 지워지고 해당 스택 메모리또한 해제된다.
fn1 함수는 실행 후 변수 a를 반환하고, 전역변수 res에 저장하므로 current contect에 global context가 위치한다.
또 GC로 표시된 모든 변수는 free된다.
위 설명을 정리하면
1. JS의 GC 메커니즘은 자동으로 실행된다
2. 로컬 스코프를 떠난 후 해당 스코프의 변수가 외부 스코프에서 참조되지 않으면 나중에 지워진다.
그래서 어떤 상황에서 메모리릭이 발생하나?
각 케이스마다 예제를 모두 다루기에는 너무 글이 길어질거 같아서 이만 줄이려 하지만, 공통점은 주로 더 이상 사용하지 않지만, 의도하지 못한 사이에 계속 참조되고 있어서 free하면 안되는 상황
이 발생될때 GC가 돌아도 free되지 못하는 memory들이 생겨났다.
당연한 이야기지만 저런 memory들이 쌓이면 성능에도 좋지 못한 영향을 주기때문에 코드를 작성하면서도 로그를 찍는 로직은 dev일때만 하도록 하거나, 구조를 잘 잡아야 겠다는 경각심이 또 한번 생겼다.