V8 의 가비지 수집 전략은 세대 가설에 기반을 둔 세대 재활용 메커니즘을 기반으로 합니다. 이 가설에는 두 가지 특징이 있습니다.
이 이론에 따르면 현대 가비지 수집 알고리즘은 객체의 생존 시간에 따라 메모리를 세대로 나누고 여러 세대의 메모리에 대해 서로 다른 효율적인 알고리즘을 사용하여 가비지 수집을 수행합니다.
V8 에서 메모리는 신세대 (new space) 와 노생 (old space) 으로 나뉜다. 다음과 같은 특징이 있습니다.
V8 힙의 공간은 신생대 공간에 낡은 세대 공간을 더한 것과 같습니다. -max-old-space-size 명령을 통해 재래식 공간의 최대값을 설정하고-max-new-space-size 명령을 사용하여 신생대 공간의 최대값을 설정할 수 있습니다. 재래식과 신생대의 공간 크기는 프로그램이 초기화될 때 설정되며 일단 적용되면 동적으로 변경할 수 없습니다.
기본적으로 64 비트 시스템의 재래식 크기는 1400M 이고 32 비트 시스템은 700M 입니다.
신세대의 경우 두 개의 reserved_semispace_size 로 구성됩니다. 각 reserved_semispace_size 의 크기는 서로 다른 자릿수의 시스템에서 다릅니다. 기본 설정은 각각 64 비트 및 32 비트 시스템에서 16MB 및 8MB 입니다. 우리는 신생대, 재세대, reserved_semispace_size 공간 크기를 아래 표에 요약했다.
가비지 수집 알고리즘을 소개하기 전에' 전체 일시 중지' 에 대해 알아보겠습니다. 가비지 수집 알고리즘은 실행하기 전에 응용 프로그램 논리를 일시 중지하고 가비지 수집을 수행한 후 응용 프로그램 논리를 실행해야 합니다. 이러한 동작을' 완전 정지' 라고 합니다. 예를 들어, GC 한 번에 50ms 가 필요한 경우 애플리케이션 로직은 50ms 를 일시 중지합니다.
전체 일시 중지의 목적은 애플리케이션 논리가 가비지 수집기에서 보는 것과 일치하지 않는 문제를 해결하기 위한 것입니다. 예를 들어, 뷔페식당에서 밥을 먹고 반갑게 음식을 챙기고 돌아왔을 때, 자신의 식기가 종업원에게 치워졌다는 것을 알게 되었다. (데이비드 아셀, Northern Exposure (미국 TV 드라마), 음식명언) 여기서 종업원은 쓰레기 수거기와 같고, 식기는 배급 대상과 같다. 우리는 바로 응용논리다. 우리의 의견으로는, 식기를 테이블 위에 임시로 올려놓았을 뿐이지만, 종업원은 네가 더 이상 사용할 필요가 없다고 생각하여 가져갔다. 너와 종업원이 같은 것에 대해 본 상황이 일치하지 않아 종업원이 우리와 기대하지 않는 일을 하게 되었다. 따라서 응용 프로그램 논리가 가비지 수집기에서 보는 것과 일치하지 않도록 가비지 수집 알고리즘이 실행될 때 응용 논리를 중지해야 합니다.
신세대 객체는 주로 Scavenge 알고리즘을 통해 가비지 수집을 수행합니다. Scavenge 의 구체적인 구현은 주로 Cheney 알고리즘을 채택하고 있다.
Cheney 알고리즘은 복제 방식으로 가비지 수집을 수행합니다. 힙 메모리를 두 개로 나눕니다. 각 공간 부분을 semispace 라고 합니다. 이 두 공간은 한 공간만 사용 중이고 다른 공간은 유휴 상태입니다. 사용 중인 semispace 를 "시작 공간" 이라고 하고 유휴 semispace 를 "끝 공간" 이라고 합니다.
프로세스는 다음과 같습니다.
객체 승진 조건은
1) 객체가 Scavenge 재활용을 거쳤는지 여부입니다. 개체가 From 공간에서 To 공간을 복사할 때 개체의 메모리 주소를 검사하여 개체가 한 번 Scavenge 재활용되었는지 확인합니다. 경험한 경우 From 공간에서 이전 세대로 개체를 복사합니다. 경험이 없다면 To 공간으로 복사하십시오.
2)To 공간의 메모리 사용량이 제한을 초과하는지 여부. From 공간에서 To 공간으로 개체를 복사할 때, To 공간이 25 개 이상 사용되면 해당 개체는 이전 세대로 직접 승격됩니다.
25 로 설정된 비율은 Scavenge 재활용이 완료되면 To 공간이 From 공간으로 반전되어 개체 메모리 할당이 계속되기 때문입니다. 비율이 너무 크면 후속 메모리 할당에 영향을 미칩니다.
대상이 재학생으로 승진하면 새로운 가비지 수집 알고리즘 처리를 받게 된다. 다음 그림은 Scavenge 알고리즘의 객체 프로모션 순서도입니다.
Scavenge 알고리즘의 단점은 해당 알고리즘 메커니즘이 메모리 공간의 절반만 활용할 수 있도록 결정한다는 것입니다. 그러나 신세대 중 개체의 생존 주기가 짧고 생존 개체가 적고 개체 복제 비용이 많이 들지 않아 이 시나리오에 적합하다.
재세대 중 대상은 두 가지 특징이 있다. 첫 번째는 생존 대상이 많고 두 번째는 생존 기간이 길다는 것이다. 낡은 세대에서 Scavenge 알고리즘을 사용하여 가비지 수집을 하면 생존 대상을 복제하는 것이 비효율적이고 공간의 절반을 낭비할 수 있습니다. 이에 따라 V8 은 재래세대에서 마크 스웨프 (Mark-Sweep) 와 마크 컴팩 (Mark-Compact) 알고리즘을 사용하여 가비지 수집을 했다.
마크 스웨이p, 표시 지우기의 의미. 주로 표시 및 지우기의 두 단계로 나뉩니다.
Scavenge 알고리즘과 달리 Mark-Sweep 은 메모리를 둘로 나누지 않으므로 공간을 낭비하지 않습니다. 그러나 Mark-Sweep 을 한 번 경험하면 메모리 공간이 불연속적이 되어 후속 메모리 할당에 문제가 발생할 수 있습니다. 예를 들어, 더 큰 개체를 할당해야 할 때 단편적인 내부 지원 할당이 없으면 가비지 수집이 필요하지 않지만 가비지 수집이 미리 트리거됩니다. (윌리엄 셰익스피어, 가비지, 가비지, 가비지, 가비지, 가비지, 가비지, 가비지, 가비지)
메모리 조각화 문제를 해결하기 위해 메모리 활용도를 높이기 위해 마크-콤팩트 (Mark-Compact) 알고리즘이 도입되었습니다. 마크-콤팩트는 마크 스웨프 알고리즘에서 향상되었습니다. 마크 단계는 마크 스웨이프와 동일하지만 태그가 지정되지 않은 객체는 다르게 처리됩니다. 마크 스웨이트와 함께 태그가 지정되지 않은 객체를 즉시 회수하는 반면 마크 컴패트는 살아 있는 객체를 옆으로 옮긴 다음 끝 경계 밖의 메모리를 정리합니다.
Mark-Compact 는 객체 이동이 필요하기 때문에 Mark-Sweep 보다 실행 속도가 느립니다. 따라서 V8 은 주로 Mark-Sweep 알고리즘을 사용하고 공간 메모리 할당이 부족할 경우 Mark-Compact 알고리즘을 사용합니다.
신세대에서는 생존 대상이 적고 가비지 회수가 효율적이며 전체 일시 중지 시간이 짧아 영향이 적다. 그러나 진부한 세대 중에는 생존 대상이 많고 쓰레기 수거 시간이 길어 완전 정지로 인한 영향이 크다. 전체 일시 중지 시간을 줄이기 위해 V8 은 표시를 최적화하여 일시 중지의 표시 프로세스를 여러 개의 작은 단계로 나눕니다. 작은 단계를 수행할 때마다 응용 프로그램 로직을 잠시 실행하여 여러 번 교대로 표시를 완료할 수 있습니다. (데이비드 아셀, Northern Exposure (미국 TV 드라마), 실행명언) 아래 그림과 같이
긴 GC 는 애플리케이션 일시 중지 및 응답 없음, 나쁜 사용자 환경을 초래할 수 있습니다. 2011 년부터 V8 은' 완전 일시 중지' 표시를 증분 표시로 바꿨다. 개선된 표시 방식, 최대 정지 시간을 원래의 1/6 로 줄입니다.
가비지 수집 원칙은 복잡하므로 이해하는 데 약간의 노력이 필요합니다. GC 원리를 이해하면 NodeJS 프로젝트에 대한 성능 병목 현상을 파악하고 튜닝하는 데 도움이 됩니다. 문장 설명 알고리즘은 V8 에서 사용되는 기본 알고리즘으로, 현대 V8 엔진은 가비지 수집을 많이 개선했습니다. 예를 들어 Chrome 64 및 Node.js v10 에서 V8 은 "병렬 태그 지정" 기술을 활성화하여 태그 시간을 60~70 으로 단축했습니다. 또' Parallel Scavenger' 기술은 신세대 가비지 수집 시간을 20~50% 단축한다.
가비지 수집은 서비스 성능에 영향을 미치는 요소 중 하나이며 서비스 성능을 향상시키기 위해서는 가비지 수집 횟수를 최소화해야 합니다.
V8 힙 메모리 최대값은 64 비트 시스템의 경우 1464MB, 32 비트 시스템의 경우 732MB 입니다. 계산 공식은 다음과 같습니다.