티스토리 뷰
메모리의 생명 주기
JS에서, 변수나 함수 등 생각할 수 있는 모든 것들을 만들 때 JS엔진은 이에 대한 메모리를 할당하고 더 이상 필요하지 않으면 해제한다.
메모리 할당은 메모리에 공간을 예약하는 과정이며, 메모리를 해제하면 공간이 확보되어 다른 용도로 사용할 수 있게 된다.
변수를 할당하거나 함수를 생성할 때마다, 메모리는 항상 다음과 같은 단계를 거치게 된다.
- 메모리 할당
JS는 생성한 객체에 필요한 메모리를 할당한다. - 메모리 사용
코드에서 명시적으로 수행되는 작업으로, 메모리를 읽고 쓰는 작업은 곧 변수에서 읽거나 변수에 쓰는 작업을 의미한다. - 메모리 해제
이 단계는 JS엔진에서도 처리된다.
이때 '객체'는 메모리 관리의 맥락에서 자바스크립트의 객체(Object)뿐만 아니라
함수와 함수의 스코프까지 포함하는 개념이다.
스택과 힙
스택: 정적 메모리 할당
- 스택에는 정적 데이터가 저장된다.
- 정적 데이터라는 것은 컴파일 과정에서 크기가 정해져 있는 데이터를 말한다.(고정된 크기의 메모리를 할당)
- 원시 값(number, string, boolean, undefined, null)과 객체 및 함수를 가리키는 참조(reference)가 해당한다.
힙: 동적 메모리 할당
- 자바스크립트가 객체 및 함수를 저장하는 또 다른 공간이다.
- 스택과 달리 JS엔진은 고정된 크기의 메모리를 할당하지 않는다. 대신 필요에 따라 더 많은 공간을 할당한다.
- 런타임 과정에서 크기를 알 수 있다.
const person = {
name: 'John',
age: 20,
};
자바스크립트는 힙에서 이 객체에 대한 메모리를 할당한다. 실제 값은 원시 값이므로, 스택에 저장된다.
const hobbies = ['hiking', 'running'];
배열도 객체이므로 힙에 저장된다.
let name = 'seungho'; // 메모리 할당
const age = 20; // 메모리 할당
name = 'Han seungho'; // 새로운 메모리 할당
const firstName = name.slice(0,4); // 새로운 메모리 할당
원시값은 불변(immutable)이다. 즉, 자바스크립트는 원래의 메모리에 변경하는 대신 새 메모리 메모리에 할당합니다.
자바스크립트의 참조
모든 변수는 먼저 스택을 가리킨다. 원시 값이 아닌 경우 스택에는 힙의 객체에 대한 참조가 포함된다.
힙의 메모리는 특정한 방식으로 정렬되지 않으므로, 스택에 대한 참조를 유지해야 한다. 참조는 '주소'로, 힙의 객체는 이러한 주소가 가리키는 '집'으로 생각할 수 있다.
위에서 다른 값들이 어떻게 저장되는지를 살펴볼 수 있다. person과 newPerson 둘 모두 같은 객체를 가리킨다.
가비지 컬렉션
JS가 모든 종류의 객체에 메모리를 할당하는 방법을 알게 됐다. 메모리의 생명주기에 따르면 아직 마지막 단계가 남아있다. 바로 메모리 해제이다.
메모리 할당과 마찬가지로 JS엔진에서 이 단계를 처리한다. 구체적으로 말하자면 가비지 컬렉터가 이를 처리한다.
JS엔진이 어떤 변수나 함수가 더 이상 필요하지 않다는 것을 인식하게 되면, 이것들이 차지하던 메모리를 해제한다.
그런데 중요한 문제는 메모리가 여전히 필요한지에 대한 여부를 결정할 수가 없다는 것이다.
즉, 더 이상 불필요한 메모리가 사용되지 않는 정확한 순간에 쓰레기를 수집해낼 수 있는 알고리즘이 존재 할 수 없음을 의미한다.
일부 알고리즘은 이러한 문제에 대해 훌륭한 근사해를 제시한다. 아래에서 가장 많이 사용되는 알고리즘인 Reference - counting garbage collection과 Mark-and-Sweep 알고리즘에 대해 이야기해 보겠다.
1. Reference-counting (참조-세기 가비지 컬렉션)
- 객체가 생성될 때마다 참조 횟수를 카운트
- 참조 되지 않을때마다 참조 횟수 감소
- 참조 횟수가 0이 되면 객체를 메모리에서 해제한다
- 간단하고 빠른 알고리즘이지만, 순환참조(circulation reference)가 발생할 경우 메모리 누수가 발생할 수도 있음
2. Mark and Sweep
- 루트에서부터 해당 객체에 접근 가능한지를 해제의 기준으로 삼는다.
- GC Root로부터 모든 변수를 스캔(그래프 순회 방식) 하면서 각각 어떤 객체를 참조하고 있는지 찾아서 마킹한다.
- 이후 마크되지 않은 객체(연결되지 않은 객체)를 스위핑하여 사용되지 않은 객체를 찾아 제거하여 메모리에서 해제한다.
- 의도적으로 GC를 실행시켜줘야한다. -> 어느 순간에 애플리케이션이 GC에게 컴퓨터 리소스를 내줘야 한다는 뜻
=> 최적화를 잘해야 한다.
- Total
- Today
- Yesterday
- e.preventDefault()
- innerText
- getCurrentPosition
- 얕은복사
- setTimeout
- react
- getitem
- 호이스팅
- var
- padStart
- Navigator
- 깊은복사
- 데드락
- Hook
- createElement
- setinterval
- Geolocation
- 브라우저 저장소
- classList
- Let
- new Date()
- setitem
- removeitem
- Return
- 스코프
- const
- console.log
- useState
- 교착상태
- localStorage
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |