nomadcoder react study를 진행하는 과정중에 typescript를 공부하면서 몇가지 코드를 작성하는 코드챌린지를 진행했었다. (3주차)
그 과정에서 한가지 의문이 가는 부분이 있었다.
(진행했던 코드 챌린지를 자세히 말하면 다음기수나 운영진분들에게 피해가 있을거라고 생각해서 자세히 말하지는 않겠다🙂)
dict를 구현하는 과정에서 clear라는 method를 만들었는데, 해당 method가 실행되면 dict 전체를 초기화하는 간단한 로직을 구현하면 되었다.
관련해서 해당 로직을 구현하기 위해서 떠오른 생각은 다음과 같았다.
1. 해당 object를 단순히 empty object를 새로 할당해버리자.
2. 모든 key를 순회해 delete 하자.
일단 프로그래밍을 C언어로 시작했기에 memory allocation과 free를 주물럭주물럭 거렸던 기억과함께, 공교롭게도 이전직장에서 담당 했던일이 memory, performance였다.
그래서 메모리 소리만들어도 일단 인상이 찌푸려지는 지경에 이르렀단말이지? (GC 싫어잉...)
따라서, 1번처럼 빈 오브젝트를 할당하면 linear하게 순회하면서 delete할 필요가 없으니 속도는 빠르겠지, 다만 기존에 참조하던 값들은 그대로 남아있을터이니 memory leak을 유발하면 안되지!!
라는 생각이 들어서 2번으로 구현을했다.
(결론부터 말하면 delete를 사용해서 삭제해도 memory free는 안되는거같다. ㅋㅋ)
The delete operator removes a property from an object. If the property's value is an object and there are no more references to the object, the object held by that property is eventually released automatically.
여하튼, 서론이 길어졌는데 자바스크립트에서 메모리를 어떤식으로 관리하는지 궁금해졌기에 한번 찍먹이라도 해보려고 한다.
그리고 역시나 생각처럼 쉬운내용은 아닌듯하기도 하고 1개글에 다 정리하기도 어려워 보인다...허허
C같은 low level language에서는 memory management를 위해서 malloc()
과 free()
를 사용한다. (크으 추억...)
반면에 자바스크립트는 객체가 생성되었을때 자동으로 메모리를 할당하고 더 이상 필요하지 않을때 자동으로 제거한다(GC)
이런 자동 메모리 관리는 개발자가 메모리 관리에 대해 고민할 필요가 없다는 잘못된 인식을 심어줄 수 있다. (괴장히 편협한 내 경험에는 98%는 진짜 고민조차 안했던거 같음🤷♂️)
메모리 생존주기는 대부분의 프로그래밍 언어에서 비슷하다.
2번 항목의 경우 모든 언어에서 명시적으로 사용하나, 1번과 3번은 low level의 언어에서는 명시적이지만, 자바스크립트같은 high level에서는 암묵적으로 작동한다.
프로그래머가 메모리 할당을 신경쓸 필요가 없도록, 자바스크립트는 값을 선언할때 자동으로 메모리를 할당한다.
const n = 123; // 정수를 담기 위한 메모리 할당
const s = "azerty"; // 문자열을 담기 위한 메모리 할당
const o = {
a: 1,
b: null,
}; // 오브젝트와 그 오브젝트에 포함된 값들을 담기 위한 메모리 할당
// (오브젝트처럼) 배열과 배열에 담긴 값들을 위한 메모리 할당
const a = [1, null, "abra"];
function f(a) {
return a + 2;
} // 함수를 위한 할당(함수는 호출 가능한 오브젝트)
// 함수식 또한 오브젝트를 담기 위한 메모리를 할당합니다.
someElement.addEventListener(
"click",
() => {
someElement.style.backgroundColor = "blue";
},
false,
);
함수 호출의 결과 메모리 할당이 일어나기도 한다.
const d = new Date(); // Date 개체를 위해 메모리를 할당
const e = document.createElement("div"); // DOM 엘리먼트를 위해 메모리를 할당
메소드가 새로운 값이나 오브젝트를 할당하기도 한다.
const s = "azerty";
const s2 = s.substr(0, 3); // s2는 새로운 문자열
// JavaScript에서 문자열은 immutable 값이기 때문에,
// 메모리를 새로 할당하지 않고 단순히 [0, 3] 이라는 범위만 저장합니다.
const a = ["ouais ouais", "nan nan"];
const a2 = ["generation", "nan nan"];
const a3 = a.concat(a2);
// a 와 a2 를 이어붙여, 4개의 원소를 가진 새로운 배열
값 사용이란 기본적으로 할당된 메모리를 읽고 쓰는걸 의미하는데, 변수나 객체 속성의 값을 읽고 쓰거나 함수 호출시 함수에 인수를 전달하여 수행할 수 있다.
위에서 언급한 것처럼 "더 이상 필요하지 않은" 모든 메모리를 찾는건 비결정적 문제다.
(개인적으로 어떤게 더 이상 필요하지 않은것인지 판단하는것도 어렵지만, manual trigger가 안되는것도 아쉽다고 생각된다.
물론, 개발자가 리소스를 낭비하지 않도록 구현하는게 가장 우선이라고 생각한다.)
가비지 콜렉션 알고리즘의 핵심 개념은 참조다.
메모리 관리 맥락에서 A라는 메모리를 통해서 B라는 메모리에 접근할 수 있다면, B는 A에 참조된다
라고 말한다.
이 알고리즘은 더 이상 필요없는 객체
를 도달할 수 없는 객체
로 정의한다.
이 알고리즘은 roots
라는 객체의 집합을 가지고 있는데, 자바스크립트에서 root는 전역 객체이다.
주기적으로 GC는 roots부터 시작해서 roots가 참조하는 객체들, 또 이 객체가 참조하는 객체들을 찾는다.
그래서 root부터 시작해서 GC는 도달할 수 있는 모든 객체를 찾고, 도달할 수 없는 모든 객체들을 수집한다.
결과적으로 GC는 도달할 수 없는 객체들의 할당되었던 메모리를 회수한다.