-
[TIL] Javascript의 Map과 WeakMapTIL-sparta 2024. 4. 20. 19:23
--
학습 키워드: javascript, map, weakmap, memory leak, caching
1. Map과 WeakMap
let map = new Map(); let hi = {name : "hi"}; map.set(hi, "asdf"); hi = null; // map의 기능들을 이용하면 {name : "hi"}가 여전히 참조 가능하다.
- Map 에서는 위와 같이 맵의 key 값인 hi 를 맵 등록 이후에 null로 바꾸어도 기존 hi의 값이 Garbage Collection 대상이 되지 않고 map이 Garbage Collection 대상이 되기 전까지 메모리에 남는다. 따라서 제대로 처리하지 못하면 memory leak의 원인이 될 수 있다고 한다.
- 이러한 특징은 일반 배열(array)이나 set 등의 자료구조에서도 똑같이 적용된다. 이게 javascript에서만 적용되는 특징인지는 추가로 알아봐야겠다.
let weakMap = new WeakMap(); let hello = {name : "hello"}; weakMap.set(hello, "qwer"); hello = null; // {name : "hello"}가 도달 불가능한 것으로 판단되어 weakMap에서 사라진다.
- 반면에, WeakMap의 경우 바깥의 참조값이 null로 변한 경우, 다른 곳에서 참조하고 있지 않는 한 weakMap 내부의 hello 가 Garbage Collection 대상이 되므로 memory leak 관리가 쉬워진다.
그 외 차이점:
- WeakMap에서 primitive type( string, number, bigint, boolean, undefined, symbol, null )은 key 값으로 설정될 수 없다고 한다. 이는 primitive 자료형의 생성이 arbitrary 하게 이루지기 때문에, 해당 값을 다른 객체 등이 참조하게 될 수 있으며, 따라서 해당 key가 GC로부터 보호받게 될 수도 있기 때문이라고 한다.
- 다만 symbol의 경우 non-registered 상태일 때만 제한적으로 key 값으로 할당이 가능하다고 한다. 이는 해당 상태일 때 symbol은 고유(unique)하기 때문이다. 자세한 사항은 아래 링크의 Shared Symbols in the global Symbol Registry 항목을 참고하자.
- WeakMap은 내부 키 값의 목록을 따로 보관하지 않는다고 한다. 이는 만약 해당 목록을 추적하려면 Garbage Collection으로 부터의 보호가 이루어져 WeakMap이 WeakMap인 이유가 사라지기 때문이라고 한다. 그러므로 만약 key 값의 list가 필요한 경우 Map을 사용하도록 한다.
2. WeakMap을 이용한 Caching
- WeakMap의 특징을 활용하는 한 가지 방법으로 Caching이 있다.
// cache.js let cache = new WeakMap(); let ind = 0; fucntion doSomething(data) { if (!cache.has(data)) { let result = ind++; // 고유성 테스트를 위해 선언될때마다 ind 값을 늘림 cache.set(data, result); } return cache.get(data); }
// main.js let data = {name:"dkim"}; let data2 = {name:"dkim"}; console.log(data == data2); // false console.log(data === data2); // false let res1 = doSomething(data); let res2 = doSomething(data); // res1과 같은 key값으로, 캐시 내 같은 값을 참조 let res3 = doSomething(data2); // res1, res2와 다른 key 값으로, 새로운 key가 cache에 추가됨 console.log(res1); // 0 console.log(res2); // 0 console.log(res3); // 1, data와 data2는 각각이 고유하기 때문에 다른 값으로 취급됨 data = null; // cache 내 data key값이 도달 불가로 GC 처리되어 cache에서 사라짐 // res값들과 cache 내의 data2 key 값은 여전히 존재함
- data가 null이 되면서 {name:"dkim"} 으로의 참조가 사라졌다. 같은 값을 가진 data2가 존재하지만 data2와 data는 엄연히 다른 객체로 고유하기 때문에 cache 내 data key값은 더이상 참조할 수가 없다. 따라서 cache에 저장되어있던 {name:"dkim"}이 도달 불가로 GC처리되어 사라진다.
- data2의 {name:"dkim"} 키 값은 앞서 언급했듯이 data의 값이었던 {name:"dkim"}과는 값만 같고 실제론 전혀 다른 고유한 객체이기 때문에 cache 내에서 사라지지 않는다.
- cache에서 data가 사라지더라도 res1과 res2의 값은 그대로 유지된다.
cache.js 의 result가 data인 경우:
let cache = WeakMap(); function doSomething(data) { let result = data; // 아무런 추가 없이 data를 value로 사용 cache.set(data, result); return cache.get(data); } let obj = {hi:"hello"}; let res = doSomething(obj); obj = null; console.log(cache.get(res)); // res가 obj와 동일한 객체를 참조하기 때문에 도달 가능하여 GC가 동작하지 않게되고, // 따라서 cache에서 해당 key가 사라지지 않고 여전히 참조가 가능하다.
- 이러한 특징을 실제 프로젝트에서 어떻게 응용할 것인지 생각해보도록 하자.
728x90'TIL-sparta' 카테고리의 다른 글
[TIL] 스파르타) Javascript 문법 종합반 1주차 수강, TMDB 개인 과제 완료 (0) 2024.04.22 [TIL] 스파르타) 웹개발 종합반 완강, MySQL Query SELECT 연습 01 (0) 2024.04.21 [TIL] 스파르타) project1 종료, jQuery, AJAX, Fetch (0) 2024.04.19 [TIL] 스파르타) 미니 프로젝트 1 (2) 2024.04.18 [TIL] 웹개발 종합반 3~5주차, CSS를 이용한 Container 회전 (0) 2024.04.17