이 섹션에서는 메모리 분석에 사용되는 일반적인 용어를 설명하며 여러 언어의 다양한 메모리 프로파일링 도구에 적용할 수 있다
메모리를 기본 유형 (예: 숫자 및 문자열)과 객체 (연관 배열)가 있는 그래프로 생각하자
다음과 같이 상호 연결된 여러 점의 그래프로 시각적으로 표현할 수 있습니다.
객체는 두 가지 방법으로 메모리를 보유할 수 있다
DevTools에서 힙 프로파일러를 사용하면 몇 가지 다른 열의 정보를 보게 될 가능성이 높다
그중 가장 눈에 띄는 두 가지는 Shallow Size와 Retained Size이지만, 이것이 의미하는 바는 무엇일까요?
객체 자체가 보유한 메모리 크기
일반적인 JavaScript 객체에는 설명 및 즉시 값을 저장하기 위한 약간의 메모리가 있다
일반적으로 배열과 문자열만 상당히 얕은 크기를 가질 수 있다 그러나 문자열과 외부 배열은 렌더러 메모리에 기본 저장소를 두는 경우가 많으므로 JavaScript 힙에 작은 래퍼 객체만 노출된다
렌더기 메모리는 검사된 페이지가 렌더링되는 프로세스의 모든 메모리로, 네이티브 메모리 + 페이지의 JS 힙 메모리 + 페이지에서 시작한 모든 전용 작업자의 JS 힙 메모리다
그럼에도 불구하고 작은 객체라도 많은 양의 메모리를 보유할 수 있는데, 이는 자동 가비지 컬렉션 프로세스에서 다른 객체가 폐기되지 않도록 하기 때문이다
객체 자체가 GC 루트에서 도달할 수 없게 된 종속 객체와 함께 삭제되면 확보되는 메모리 크기
GC 루트는 네이티브 코드에서 V8 외부의 자바스크립트 객체를 참조할 때 생성되는 핸들로 구성된다(로컬 또는 전역). 이러한 모든 핸들은 GC 루트 > 핸들 범위 및 GC 루트 > 전역 핸들 아래의 힙 스냅샷 내에서 찾을 수 있다
브라우저 구현을 자세히 다루지 않고 이 문서에서 핸들을 설명하면 혼란스러울 수 있다
GC 루트와 핸들은 둘 다 개발자가 신경 쓰지 않아도 되는 부분이다
많은 내부 GC가 있고 대부분은 사용자가 별 관심을 기울이지 않는다
애플리케이션 관점에서 보면 다음과 같은 종류의 루트가 있다
Window 전역 객체 (각 iframe에 있음) 힙 스냅샷에는 창에서 가장 짧은 유지 경로에 있는 속성 참조의 거리 필드가 있다
문서를 순회하여 도달할 수 있는 모든 네이티브 DOM 노드로 구성된 문서 DOM 트리다
일부는 JS 래퍼가 없을 수도 있지만 래퍼가 있으면 문서가 활성화된 동안 래퍼가 활성화된다
경우에 따라 디버거 컨텍스트 및 DevTools 콘솔에 의해 객체가 유지될 수 있다 (예: 콘솔 평가 후). 콘솔을 비우고 디버거에 활성 중단점이 없는 상태에서 힙 스냅샷을 생성한다
메모리 그래프는 루트로 시작되며 브라우저의 window 객체 또는 Node.js 모듈의 Global 객체일 수 있, 이 루트 객체가 GC되는 방식은 제어할 수 없다
루트에서 연결할 수 없는 것은 무엇이든 GC가 된다
힙은 상호 연결된 객체의 네트워크다
수학에서는 이 구조를 그래프 또는 메모리 그래프라고 한다
그래프는 에지로 연결된 노드로 구성되며, 두 노드 모두에 라벨이 지정된다
노드 (또는 객체)에는 노드를 빌드하는 데 사용된 생성자 함수의 이름을 사용하여 라벨이 지정된다
에지는 속성의 이름을 사용하여 라벨이 지정된다
아래의 힙 프로파일러 기록에서 눈길을 끄는 요소로는 거리, 즉 GC 루트로부터의 거리가 있다
동일한 유형의 거의 모든 객체가 같은 거리에 있고 몇 개가 더 멀리 있다면 조사해 볼 가치가 있다
도미네이터
도미네이터 객체는 각 객체에 정확히 하나의 도미네이터가 있기 때문에 트리 구조로 구성됩니다. 객체의 도미네이터에는 자신이 지배하는 객체에 관한 직접 참조가 없을 수 있습니다. 즉, 도미네이터의 트리는 그래프의 스패닝 트리가 아닙니다.
아래 다이어그램에서:
노드 1은 노드 2를 지배한다
노드 2는 노드 3, 4, 6을 지배한다
노드 3은 노드 5를 지배한다
노드 5는 노드 8을 지배한다
노드 6은 노드 7을 지배한다
메모리를 프로파일링할 때 힙 스냅샷이 특정한 방식으로 보이는 이유를 이해하면 도움이 된다
이 섹션에서는 특히 V8 JavaScript 가상 머신 (V8 VM 또는 VM)에 해당하는 메모리 관련 주제를 설명한다
세 가지 기본 유형이 있다
숫자는 다음 중 하나로 저장할 수 있다
문자열은 다음 중 하나에 저장할 수 있다
새 자바스크립트 객체의 메모리는 전용 자바스크립트 힙 (또는 VM 힙)에서 할당된다
이러한 객체는 V8의 가비지 컬렉터에서 관리하므로 이에 대한 강력한 참조가 하나 이상 있는 한 활성 상태로 유지된다
네이티브 객체는 자바스크립트 힙에 없는 나머지 모든 객체이다
힙 객체와 달리 네이티브 객체는 전체 기간 동안 V8 가비지 컬렉터에 의해 관리되지 않으며 JavaScript 래퍼 객체를 사용하여 JavaScript에서만 액세스할 수 있다
Cons 문자열은 저장 후 조인되는 문자열 쌍으로 구성된 객체로, 연결의 결과
cons string 콘텐츠의 조인은 필요한 경우에만 발생한다
예를 들어 조인된 문자열의 하위 문자열을 생성해야 하는 경우가 있다
예를 들어 a와 b를 연결하면 연결 결과를 나타내는 문자열 (a, b)를 얻게 된다
나중에 d를 이 결과에 연결하면 또 다른 cons 문자열 ((a, b), d)을 얻게 된다
배열 - 배열은 숫자 키가 포함된 객체다, V8 VM에서 대량의 데이터를 저장하기 위해 광범위하게 사용된다 사전처럼 사용되는 키-값 쌍 집합은 배열로 백업된다
일반적인 JavaScript 객체는 저장에 사용되는 두 가지 배열 유형 중 하나일 수 있다
속성의 수가 극히 적은 경우 자바스크립트 객체 자체에 내부적으로 저장할 수 있다
매핑 - 객체의 종류와 레이아웃을 설명하는 객체다 예를 들어 맵은 빠른 속성 액세스를 위해 암시적 객체 계층 구조를 설명하는 데 사용된다
각 네이티브 객체 그룹은 서로에 대한 상호 참조를 보유한 객체로 구성된다
모든 노드가 상위 요소에 대한 링크와 다음 하위 요소 및 다음 동위 항목에 연결되어 연결된 그래프를 형성하는 DOM 하위 트리를 예로 들어보자
네이티브 객체는 자바스크립트 힙에 표시되지 않기 때문에 크기가 0이다
대신 래퍼 객체가 생성된다
각 래퍼 객체는 명령어를 리디렉션하기 위해 상응하는 네이티브 객체 참조를 보유한다
객체 그룹은 그 자체로 래퍼 객체를 보유한다 하지만 GC는 래퍼가 더 이상 참조되지 않는 객체 그룹을 해제할 만큼 스마트하기 때문에 수집 불가능한 주기가 생성되지는 않는다 하지만 단일 래퍼를 해제하는 것을 잊으면 전체 그룹 및 관련 래퍼가 유지된다.