자바스크립트 메모리 생존 주기 (1)
크리스는 C언어를 배우는 친구가 "나 어제 malloc하고 free 안 해서 메모리 터졌어"라고 투덜대는 걸 들었다.
크리스는 속으로 웃었다.
"ㅋㅋ 원시인인가? 자바스크립트는 그런 거 신경 안 써도 알아서 다 해주는데."
하지만 며칠 뒤, 크리스가 만든 웹 페이지를 켜놓고 점심을 먹고 왔더니 크롬 탭이 **"앗, 이런! (Aw, Snap!)"**이라며 죽어 있었다.
메모리 사용량이 4GB를 넘겨서 강제 종료된 것이다.
"알아서 다 해준다며! 왜 터지는 건데?"
자바스크립트가 메모리를 자동으로 관리해 주는 것은 맞다. 하지만 "어떻게" 관리하는지 모르면, 밑 빠진 독처럼 메모리가 새어나가는 코드를 짜게 된다.
이제 알고리즘을 넘어, 코드가 살아 숨 쉬는 공간인 **메모리(Memory)**의 세계로 들어가 보자.
1. 메모리의 삶: 태어나서, 일하고, 죽는다
프로그래밍 언어가 무엇이든, 메모리의 생존 주기(Life Cycle)는 딱 3단계로 나뉜다.
C나 C++ 같은 저수준 언어는 개발자가 이 3단계를 모두 직접 명령해야 한다. (malloc -> read/write -> free)
반면, 자바스크립트는 1번과 3번을 자동으로 처리한다. 바로 여기서 오해가 시작된다. **"자동이니까 신경 꺼도 되겠지?"**라는 착각이다.
2. 1단계: 할당 (Allocation)
자바스크립트는 우리가 변수를 선언할 때, 데이터 타입에 맞춰 알아서 메모리를 빌려온다.
JavaScript
var n = 123; // 숫자를 위한 메모리 할당 var s = "azerty"; // 문자열을 위한 메모리 할당 var o = { a: 1, b: null }; // 객체와 그 안의 값들을 위한 메모리 할당
여기서 중요한 점은 데이터가 어디에 저장되느냐다.
메모리 공간은 크게 **스택(Stack)**과 힙(Heap) 두 구역으로 나뉜다.
스택 (Stack): 정적 메모리 할당
힙 (Heap): 동적 메모리 할당
3. 2단계: 사용 (Use)
할당된 변수를 읽거나 수정하는 단계다.
JavaScript
console.log(n); // 읽기 n = 456; // 쓰기 (재할당)
우리가 코딩하면서 하는 대부분의 작업이 여기에 해당한다. 여기까지는 문제 될 게 없다.
4. 3단계: 해제 (Release) - 문제의 시작
가장 어렵고 중요한 단계다.
"더 이상 필요 없는 메모리"를 찾아서 되돌려줘야 한다. 그렇지 않으면 브라우저가 쓸 수 있는 메모리가 점점 줄어들다가 결국 뻗어버린다. 이를 **메모리 누수(Memory Leak)**라고 한다.
저수준 언어에서는 개발자가 "이제 obj는 필요 없어, free(obj)!"라고 명시하지만, 자바스크립트는 엔진 내의 **가비지 콜렉터(Garbage Collector, GC)**라는 청소부가 대신해준다.
하지만 이 청소부에게는 치명적인 한계가 있다.
"이 데이터가 앞으로 정말로 필요 없을지, 기계가 완벽하게 판단하는 것은 불가능하다."
JavaScript
let data = { value: "엄청 큰 데이터" }; // ... 코드가 길어짐 ... // 개발자: "난 이제 data 안 쓸 건데?" // GC: "어? 근데 저기 다른 함수에서 data를 참조하고 있는데? 아직 필요한가 본데?" (청소 안 함)
이런 오해(참조)가 쌓이고 쌓이면 GC는 쓰레기를 끌어안고 살게 되고, 결국 메모리가 터진다.
개발자가 GC의 작동 원리를 알아야 하는 이유가 바로 이것이다. 청소부가 헷갈리지 않게 힌트를 줘야 하기 때문이다.
5. 결론: "자동"은 "만능"이 아니다
그렇다면 GC는 도대체 어떤 기준으로 "이건 쓰레기야!"라고 판단할까?
그리고 크리스의 코드는 왜 GC를 혼란스럽게 만들었을까?
다음 글, "[JavaScript] 가비지 콜렉션(Garbage Collection): 쓰레기 청소부의 기준" 편에서 계속된다.