콜스택, 메모리힙과 메모리모델 구조
FrontEnd/웹 지식

콜스택, 메모리힙과 메모리모델 구조

728x90

일반적인 프로세스 메모리 모델의 구조는 아래와 같다.

 

 

Text : 프로그램 코드와 상수가 정의되어 있고, 읽기만 가능하다

Data : 전역변수와 정적 변수가 저장되어 있다.

Heap : 동적 메모리 호출에 의해 할당되는 메모리 영역

Stack : 함수 값, 지역변수, 반환 주소등이 저장되는 영역이다.

 

 

 

그렇다면 Node.js 에서 프로세스 메모리를 관리하는 방법은 어떨까? Node.js에서는 V8메모리 구조를 사용한다. 

 

(https://ui.toast.com/weekly-pick/ko_20200228)

 

 

V8엔진은 힙 메모리에 동적 데이터를 저장하며, 가비지 컬렉션이 발생한다. 이때 모든 영역이아니라, New와 Old 영역에서만 가비지 컬렉션이 발생한다.

 

New : 새로 만들어진 모든 객체를 저장

Old : GC(가비지 컬렉션)이 두번 발생하고 살아남은 객체들이 이동하는 영역

라지 오브젝트 : 다른 영역의 제한된 크기보다 큰 객체들이 존재

코드 : 실시간 컴파일러가 코드들을 저장

셀, 속성 셀, 맵 : Cells,PropertyCells,Maps를 포함

가장 큰 특징은 GC인듯 했다. GC가 수행되기에 힙 영역이 깨끗하게 유지되는 것이다. GC또한 마이너 GC와 메이저 GC로 나뉘게 된다.

마이너 GC : New영역을 깨끗하게 유지한다. 해당 영역의 객체들은 크기가 매우 작다.

메이저 GC : Old 영역을 깨끗하게 유지한다. 항상 진행되진 않고, V8엔진이 Old영역의 메모리가 부족하다고 판단될 때 발생한다.

 

 

New영역과 가비지 컬렉션에 대해서 조금 더 알아보자.

 

 

 

New 영역 : 새로 만들어진 모든 객체를 저장하고, 짧은 생명주기를 가진다.

Old 영역 : 마이너 GC가 두번 발생할 동안 New영역에서 살아남은 객체들이 이동하는 영역

라지 오브젝트 영역 : 다른 영역의 제한된 크기보다 큰 객체가 살고있는 영역

코드 영역 : 컴파일러가 코드들을 저장하는 영역

 

 

 

이제 우리는 가비지 컬렉션이 힙의 의미없는 저장소들을 비워주는 역할이란 사실을 안다. 그런데 이 GC또한 영역이 나누어져 있었다.

 

 

가비지 컬렉션

마이너 GC : New 영역을 작고 깨끗하게 유지시키는 GC, 새 객체에 대한 공간을 예약하려 할때, 할당 포인터가 존재하는데, New영역의 마지막에 도달하면 마이너 GC가 발생하게 된다. 이러한 과정을 스캐벤져 라고 한다.

정말 재미있는 부분은.. New 영역또한 크기가 같은 2개의 세미영역 즉, To 영역과 From 영역으로 나누어진다. 대부분의 할당은 To영역에서 만들어지며 해당 영역이 가득 차면 마이너 GC가 발생한다.

 

특히 마이너 GC가 동작하는 과정을 아래 페이지에서 좌,우 화살표를 움직이면서 확인해볼 수 있었다.

 

V8 Minor GC

 

V8 Minor GC

 

speakerdeck.com

 

 

 

메이저 GC : Old 제너레이션 영역을 작고 깨끗하게 유지시킨다. 메이저 CG는 v8엔진이 Old 영역의 메모리가 충분치 않다고 판단될 대 발생된다. 해당 영역은 동적으로 계산된 크기에 기반하며, 마이너 GC에서 채워진다..

마이너 GC의 도작 과정을 조금 더 세부적으로 봐보자.

 

 

 

  1. 많은 마이너 GC 주기를 거치고 Old 영역이 거의 다 찼으며 V8이 "메이저 GC"를 발생시킨다고 가정해보자.
  2. 메이저 GC는 스택 포인터에서 시작해 재귀적으로 객체 그래프를 순회하면서, Old 영역 내 메모리를 사용한 객체와 남아있는 객체를 가비지로 표시한다.
  3. 동시 마킹이 완료되거나 메모리 제한에 도달하면 GC는 메인 스레드를 사용하여 마킹의 마지막 단계를 수행한다. 이 때 일시 정지 시간이 발생한다.
  4. 메이저 GC는 동시 스위프 스레드를 사용해 모든 참조 없는 객체들의 메모리를 사용 가능한 상태로 표시한다. 또한 조각화를 피하기 위해 관련 메모리 블록을 동일한 페이지로 이동하도록 병렬 압축 작업도 발생한다. 포인터들은 이 세 단계를 통해 갱신된다.

 

 

 

 

 

 

힙 메모리에 자주 생성하고 해제를 반복하다보면 빈 공간 fragmentation이 생길 수 있다. malloc() 자체가 실패할 수 있는데 효율적인 방법은 무엇일까??

 

 

구현을 하면서도 계속 의아했던건, 힙에 빈공간이 남게 된다는 것이었다. 이 빈공간의 문제를 해결하기 위한 몇가지 방법이 있었다.

 

 

우선 단편화에 대해 조금 알아야한다. 단편화는 외부단편화와 내부단편화로 나뉜다.

 

 

 

내부 단편화 : 메모리를 할당할때 프로세스가 필요한 양보다 더 큰 메모리가 할당되는 상황

외부단편화 : 작은 빈공간들을 합치면 메모리를 저장할 수 있지만 쪼개져 있어서 할당을 못하는 경우

 

 

 

 

 

해결방법으로는 다음이 있다.

  1. 페이징 : 가상 메모리를 사용한다. 연속하지 않은 공간을 활용할 수 있다. 필요한 메모리를 페이지 단위로 프레임에 옮기기에 가능
  2. 세그멘테이션 : 가상 메모리를 서로 크기가 다른 논리적 단위인 세그먼트로 분할
  3. 메모리 풀 : 필요한 메모리 공간을 크기,개수만큼 사용자가 미리 할당받아서 사용하고 반납한다.

 

 

 

결론적으로 자바스크립트에서 콜스택,메모리힙 구조를 통해서 얻을 수 있는 장점은 아래와 같다..

 

콜 스택, 메모리힙 구조 덕분에 자바스크립트에서 수동적으로 메모리 할당,해제를 해야하는 번거로움에서 벗어났다

 

 

 

 

또한 힙과 스택의 차이점으로는 아래와 같은 특성을 가진다.

  1. 스택은 메모리 할당,해제가 자동으로 수행된다.
  2. 힙은 프로그래머가 수동으로 할당하고 해제한다.
  3. 스택 메모리 처리속도가 힙보다 빠르다
  4. 메모리 부족문제는 스택에서 발생활 확률이 더 높다.
  5. 힙 메모리의 주요 문제는 메모리 조각화 혹은 메모리 단편화이다.
  6. 스택은 메모리 영여깅 더 작고 캐시친화적이다.
  7. 힙 메모리는 메모리 전체에 분산되어 캐시누락을 야기한다.
  8. 스택은 유연하지 않으며 할당된 메모리 크기는 변하지 않는다.
728x90

'FrontEnd > 웹 지식' 카테고리의 다른 글

함수형 프로그래밍  (0) 2022.08.30
XML 파서  (0) 2022.08.30
캐시교체 정책  (0) 2022.08.30
GitHub Pull Request  (0) 2022.08.27
05_Webpack 여러 loader들  (0) 2022.01.15