javascript

자바스크립트 메모리 관리

노엠디엔 2023. 10. 31. 22:32

이번에 우아한 테크 자바스크립트 메모리 관리 부분에 대해 영상을 보고 정리를 하게 되었다.

우아한 테크: https://www.youtube.com/watch?v=1BoJZqxFYfQ

MDN 사이트 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Memory_management

메모리 생명 주기

  1. 메모리 할당 변수, 함수 객체 등을 만들 때 메모리를 할당
  2. 메모리 사용 변수를 읽거나 할당된 메모리를 사용
  3. 메모리 해제 더 이상 필요가 없어지면 메모리에서 해제

다른 C언어와 같은 저수준에 언어에서는 동적으로 메모리를 관리하지만

자바스크립트에서는 엔진이 모두 처리해 줌! (개발자가 직접 메모리 관리 불가!)

 

힙과 스택

원시값 : number, boolean, string, 등에 원시값들은 정적 메모리 할당으로 불리며 고정된 메모리를

할당하기 때문에 원시 값들의 크기에 제한이 있다.

참조값: 배열, 객체 등 참조값은 필요한 만큼 많은 메모리를 할당 동적 메모리 할당이라 불리며 

주소값은 스택에 저장된다.(변수의 스코프 실행 콘텍스트, 렉시컬 환경에 영향)

 

가비지 컬렉션

가비지 컬렉션은 자바스크립트 엔진이 메모리를 해제하는 방법이다.

해당 메모리에 할당된 객체나, 데이터가 더 이상 필요가 없어지면 메모리에서 해제한다.

하지만 컴퓨터는 메모리에 할당된 데이터가 필요한지 결정할 수 없다고 한다

즉 필요 없는 메모리인지 해제하는 알고리즘을 구현하기 어렵다.

 

Reference Counting 

메모리 해제하는 방식을 최대한 구현한 방식으로 자신을 참조하는 객체의 수를 세는 방식이다.

단순하게 자신을 참조하는 객체를 카운팅 하다 자신을 참조하는 객체의 수가 없어지면 가비지 켈렉션에 의해

메모리에서 해제하는 방식인 것 같다. 하지만  Reference Counting은 순환 참조 문제를 가지고 있다고 하는데

순환 참조 문제는 두 개 이상의 객체가 서로를 참조하는 상황을 나타내며, 이러한 경우에는
객체 간의 참조 횟수가 0이 되지 않아 메모리 누수가 발생할 수 있다.

 

 

Mark and Sweep

Reference Counting에 순환 참조 문제를 해결하기 위해 Mark and Sweep 메모리 해제 방식이 도입

루트에서 도달 가능한 객체를 표시하고 도달 불가능한 객체를 수거하여 메모리를 해제하는 방식

하지만 Mark and Sweep에도 단점은 있는데 정기적으로 모든 객체를 검사하고 표시해야 하므로 큰 힙(heap)에서는 성능 문제가 발생할 수 있으며  다른 문제로는 "언제 객체가 필요 없는지"에 대한 부분에서는 개선되어야 한다고 한다. 

 

V8 가비지컬렉션

V8엔진은 프로그램을 실행하면 메모리의 Resident Set이라는 빈 공간이 할당되는데 Resident Set은 스택 영역과 힙 영역으로 나눌 수 있다. 자바스크립트의 경우 싱글스레드라서 스택 메모리를 하나만 가지며 스택은 함수 호출이 끝난 후 OS에 의해 정리되지만, 힙 메모리는 그렇지 않다. 따라서 힙에 있는 쓸모없는 메모리가 해제되지 않는다면, 
메모리 누수가 발생하여 프로그램 속도는 점점 느려질 것입니다.

힙 영역은 세부적으로 New space, Old space, Large Object space, 코드 space, 셀 space, 속성 space, 맵 space로 이루어져 있다. 가비지 컬렉션이 일어나는 부분은 New space와 Old space에서 일어난다고 한다

  • New space(Young generation): 새로 만들어진 Object가 저장됩니다.
  • Old space(Old generation): New space에서 마이너 가비지 컬렉션이 2번 발생할 동안 살아남은 객체들이 저장됩니다. 이 영역은 두 개로 나눌 수 있습니다.
    • Pointer space: 다른 객체를 참조하는 객체, 즉 다른 객체에 대한 포인터를 가진 객체
    • Data space: 문자열, 실수 등의 데이터만을 가진 객체

객체는 처음에 New space의 첫 번째 semi space에 할당되며 만약 GC로부터 한 번 생존한다면, 다른 semi space로 이동하고 생존한 객체들이 또 한 번 GC로부터 생존하면, Old space로 이동한다고 함!.

 

가비지 컬렉션은 New space에서의 마이너 GC와 Old space에서의 메이저 GC로 각각 다른 방식으로 동작한다고 한다!.

The Generational Hypothesis라는 가설을 살펴보면 대부분의 경우 새로운 객체가 오래된 객체보다 쓸모없어질 가능성이 높다. 오래된 객체가 쓸모없어질 가능성이 낮은데 GC가 모든 객체를 매번 검사하는 것은 비효줄이디.

객체의 특성에 맞춰 힙 영역을 크게 New space와 Old space로 분류하여 새로운 객체는 New space에서, 오래된 객체는 Old space에서 각 영역에 최적화된 GC들(마이너, 메이저)로 괸리한다. 마이너 GC는 객체들의 생명주기가 짧은 New space에서 빠르게 가비지 컬렉션을 하고, 메이저 GC는 메모리 사이즈가 큰 Old space에서 가비지 컬렉션을 한다

1. 마이너 GC(Scavenger)

새 객체는 New 영역의 semi space에 할당되며 메모리 영역이 모두 할당되면 Minor GC가 발생한다.

살아남은 객체는 나머지 semi space로 이동한다.

 1개의 semi space는 비어있으며. 이 비어있는 영역은 To space, 객체들이 머무르는 영역은 From space라고 부릅니다

From space에서 To space로 생존한 객체들의 대피가 완료되면, From space에 남아있는 더 이상 쓸모없는 객체들을 버립니다. 마지막으로 From space와 To space의 역할을 서로 바꿔줍니다.

두 번째 Minor GC에서 살아남은 객체들은 Old 영역으로 이동하며  Old 영역에서는 동적으로

계산된 결과에 따라 공간이 부족하다고 판단될 때 Major GC가 발생한다.

 

가바지 컬렉션은 메인스레드에서 발생하기 때문에 자바스크립트가 실행되지 않을 수 있다.

이 현상을 stop the world? 현상이라 불린다.
이러한 현상을 해결하기 위해 여러 가지 최신 기술들이 추가되었다고 한다

 

Parallel

기존에는 메인 스레드 혼자 하던 일을 헬퍼 쓰레드들과 균등하게 나누어 일을 합니다. 쓰레드 간의 동기화를 처리해야 해서 오버헤드는 생기지만 stop-the-world 시간이 크게 감소합니다.

Concurrent

메인 스레드는 더 이상 가비지 컬렉션을 하지 않고, 헬퍼 스레드들이 수행합니다. 기술적으로 구현하기는 어렵지만, 메인 스레드의 stop-the-wolrd 시간이 전혀 없다는 큰 장점이 있습니다.

Idle-time GC

개발자는 GC에 직접 접근할 수 없습니다. 하지만 v8은 크롬과 같은 embedder에게 가비지 컬렉션을 유발할 수 있는 메커니즘을 제공합니다. 크롬은 프로그램이 쉬는 free나 idle time을 알 수 있습니다. 예를 들면, 1초에 60 프레임을 제공하는 크롬은 1 프레임을 렌더링 하기 위해 약 16ms(1s / 60)가 소모됩니다. 만약 애니메이션 프레임 렌더링 작업이 16ms 보다 빨리 끝나면, 크롬은 다음 프레임 작업 전까지 가비지 컬렉션을 유발합니다.

이번 영상을 통해 가비지 컬렉션에 대해 조금은 알 수 있게 되었다. 자바스크립트의 클로저나 다른 언어에서 사용되는

포인터 개념에 대해 듣게 되면서 이 가비지 컬렉션에 대해 의문점이 많이 있었는데 이번에 조금 이해하는 계기가 되었다.

영상도 퀄리티가 좋지만 구글링을 통해 블로그를 찾아보니 더 정리가 잘 되어있어서 참고블로그를 보는 것이 좋겠다!!

 

참조 블로그 : https://fe-developers.kakaoent.com/2022/220519-garbage-collection/

'javascript' 카테고리의 다른 글

실행 컨텍스트의 평가와 실행, 스택  (0) 2023.09.23
모듈의 의미  (0) 2023.08.23
제네레이터  (0) 2023.08.16
디바운스와 스로틀  (1) 2023.07.27
타이머 함수  (0) 2023.07.26