테스트 코드는 위의 두가지를 짬뽕(?)한 함수이지만 실제 동작을 판단하기 위해서는 각각 나눠서 해야할 것이다. (왜냐하면 실제 프로세스는 에러가 나는 경우 동작을 멈추기 때문이다.)
아래 프로세스를 살펴보자.
실행되고 있는 프로그램은 항상 메모리의 빈공간에 할당되어 나타난다. 이 공간은 Resident Set 이라고 불린다.
V8엔진은 자바스크립트를 크롬 브라우저에서 돌려주는 엔진이다. V8는 자바스크립트 코드를 어셈블리 코드로 컴파일해주는데, nodejs는 V8엔진을 똑 떼어 와서 자바스크립트를 실행할 수 있는 자바스크립트 런타임이다.
V8엔진은 메모리를 3개의 section으로 구분한다.
Code: 컴파일 된 코드가 실행되는 공간이다
Stack: 실행을 위한 데이터(로컬 변수) 및 포인터가 쌓이는 곳이다. 실행 후에는 사라진다
Heap:함수, 변수, 클래스의 선언들이 들어가는 곳이다.
반면에 C언어에서는 malloc()함수를 통해서 메모리상의 HEAP영역에 공간을 할당한다. 그리고 할당된 메모리에서 작업을 끝내면 free()함수를 통해서 HEAP영역에서의 공간을 해체한다. 이처럼 프로그래머는 프로세스 메모리 관리 책임을 지게 된다.
하지만 우리는 javascript에서 malloc과 free와 같은 함수를 사용해서 메모리컨트롤을 하지 않고, 프로그램이 동작하다가 HEAP영역의 공간이 부족한 현상이 발생하게 될것이다. 이러한 현상을 memory leak이라고 한다.
다행히도 V8엔진은 garbage collection을 통해서 자동으로 메모리관리를 해준다. garbage collector는 referencing 되어있지 않은 변수들을 주기적으로 메모리에서 해체시킨다.
자세한 건 아래서 알아보자.
V8은 가비지 콜렉션을 이용해서 힙 메모리를 관리한다. 간단히 얘기해, 스택에서 더이상 참조하지 않는 객체의 메모리를 해제하여 다른 객체가 메모리를 할당하여 쓸 수 있도록 한다. V8의 가비지 컬렉터는 더이상 사용하지 않는 메모리를 해제하여 공간을 확보하는 책임이 있다.
V8 가비지 컬렉터는 객체를 생성시점으로 묶어서 각각 다른 스테이지별로 별도로 관리한다. V8 가비지 컬렉터는 2개의 다른 스테이지와 세개의 다른 알고리즘을 사용한다.
V8 GC마이너
아래 소개해 드릴 값들은 그 태생부터 도달 가능하기 때문에, 명백한 이유 없이는 삭제되지 않는다.
예시:
반대로 생각하면 이런 값들의 사용을 줄여야한다.
이런 값은 루트(root) 라고 부른다.
루트가 참조하는 값이나 체이닝으로 루트에서 참조할 수 있는 값은 도달 가능한 값이 된다.
전역 변수에 객체가 저장되어있다고 가정해 보자. 이 객체의 프로퍼티가 또 다른 객체를 참조하고 있다면, 프로퍼티가 참조하는 객체는 도달 가능한 값이 된다. 이 객체가 참조하는 다른 모든 것들도 도달 가능하다고 여겨지게 되는 것이다.
객체를 '새로운 객체’와 '오래된 객체’로 나눈다. 객체 상당수는 생성 이후 제 역할을 빠르게 수행해 금방 쓸모가 없어지는데, 이런 객체를 '새로운 객체’로 구분한다. 가비지 컬렉터는 이런 객체를 공격적으로 메모리에서 제거한다. 일정 시간 이상 동안 살아남은 객체는 '오래된 객체’로 분류하고, 가비지 컬렉터가 덜 감시하게 된다.
방문해야 할 객체가 많다면 모든 객체를 한 번에 방문하고 mark 하는데 상당한 시간이 소모된다. 가비지 컬렉션에 많은 리소스가 사용되어 실행 속도도 눈에 띄게 느려지게 될 수 있다. 자바스크립트 엔진은 이런 현상을 개선하기 위해 가비지 컬렉션을 여러 부분으로 분리한 다음, 각 부분을 별도로 수행합니다. 작업을 분리하고, 변경 사항을 추적하는 데 추가 작업이 필요하긴 하지만, 긴 지연을 짧은 지연 여러 개로 분산시킬 수 있다는 장점이 있습니다.
가비지 컬렉터는 실행에 주는 영향을 최소화하기 위해 CPU가 유휴 상태일 때에만 가비지 컬렉션을 실행합니다.
출처 : https://ko.javascript.info/garbage-collection
간단히 말해 메모리 누수란, 애플리케이션에서 더이상 사용하지 않는 메모리가 힙에서 계속 남아 있고, 그래서 이를 가비지 컬렉터가 OS로 메모리로 반환하지 못하는 상황을 의미한다. 이는 메모리에서 쓸모없는 블록으로 존재하게 된다. 이러한 블록이 계속해서 생기게 되면 애플리케이션에서는 더이상 사용할 메모리가 존재하지 않게 되고, 나아가 OS 또한 할당할 메모리가 남아나지 않아서 애플리케이션이 느려지고 크래쉬되거나, 혹은 OS 단에서 문제가 발생할 수 있다.
가상메모리는 실제 시스템에 존재하는 물리메모리의 크기와 관계없이 가상적인 주소공간을 사용자 태스크에게 제공한다.
32bit의 CPU의 경우 2^32크기인 4GB를, 64bit의 CPU의 경우 2^64 크기의 주소공간을 사용자에게 할당한다.
(따라서 32bit CPU 기준으로 각 태스크마다 4GB의 공간을 가지고 있다고 할 수 있다.)
한가지 주의할점은, 물리적으로 4GB의 메모리를 전부 사용자 태스크에게 제공하는 것은 아니라는 점이다.
4GB라는 공간은 프로그래머에게 개념적으로 제공하는 공간이며, 실제로는 '사용자가 필요한 만큼'의 물리메모리를 제공한다.
따라서 가상메모리는 개념적으로 4GB 라는 큰 공간을 제공함과 동시에 물리메모리는 필요한 만큼의 메모리만 사용되므로 가능한 많은 태스크가 동시에 수행될 수 있다는 장점을 제공한다.
리눅스에서 가상메모리를 스왑파일시스템이라고 한다. 일반 파일시스템과는 다소 다른 용도로 사용되는데 실제메모리의 보조역할하는 것으로 하드디스크의 일부를 메모리처럼 사용한다.
swap메모리는 하드디스크에 swap파일시스템장치를 실제메모리가 모자랄경우 메모리처럼 사용한다.
따라서 실제메모리(RAM)에 비해 하드디스크를 이용하기때문에 속도면에서는 현저히 떨어지게 된다.
또한, 실제메모리가 부족하다는 것은 현재 시스템이 어느정도 부하가 있다는것으로, 시스템 안정성 면에서는 그다지 좋지 못하다는 뜻이다.
하지만 swap메모리가 없을경우 실제메모리가 부족하여 시스템이 다운되거나 장애가 발생하는것보단 swap메모리를 두어 보다 안정성있게 가용하는 편이 좋을것이다.
process 모듈은 require()를 사용하지 않고도 Node.js 애플리케이션에 접근할 수 있는 전역객체다. 동작중인 프로세스에 접근할 수 있는 권한을 제공한다.
process 모듈은 현재 프로세스를 중단시키고, 또 다른 프로세스를 종료하고, 이벤트 큐에 동작을 등록시킨다. 현재 Node.js 프로세스를 종료시키고 싶다면 process.exit()
process 모듈의 정보 가져오기
process 모듈은 동작하는 프로세스 및 시스템 아키텍처에 관한 풍부한 정보를 가지고 있다. 예를 들어 process.pid
속성은 프로세스 ID 값을 전달하고, 애플리케이션에서는 이 ID 값을 활용할 수 있다.
운영체제의 기본에 대한 이해가 많이 부족하다는 느낌을 받았다. 이해하는 데에만 많은 시간을 써서 코드를 개선하거나 관련 정보를 수집 정리하지 못했다. 한 주를 마무리하며 느끼는 것은 나무를 보지 말고 숲을 봐야겠다는 성찰과 겸손함이었다.