이전 글에서 원시 타입(Primitive Types)과 참조 타입(Reference Types)의 구분 및 숫자 타입의 메모리 처리 방식을 살펴보았습니다. 이번 섹션에서는 문자열(String)에 대해 V8 엔진이 어떻게 메모리를 관리하고 최적화하는지 알아보겠습니다.
자바스크립트에서 문자열은 원시 타입(Primitive Type)에 속하며, 값의 불변성과 효율적인 메모리 관리가 중요한 역할을 합니다. V8 엔진은 문자열을 효율적으로 관리하기 위해 다양한 내부 구조와 최적화 기법을 사용합니다. 특히, 문자열의 생성 방법에 따라 내부 구조가 다르게 관리되며, 이에 따른 메모리 할당 방식도 달라집니다.
문자열 생성 방법에 따른 내부 구조의 차이
자바스크립트에서 문자열을 생성하는 방법에 따라 V8 엔진은 내부적으로 문자열을 관리하는 방식이 달라집니다. 이를 예시를 통해 살펴보겠습니다.
class StringMemorySnapshot {
constructor() {
// 약 10MB 를 갖는 정적 문자열
this.string1 = 'a'.repeat(10 * 1024 * 1024);
this.string2 = 'a'.repeat(10 * 1024 * 1024);
// 약 10MB 를 갖는 정적 문자열
this.string3 = 'bbb ...' // 약 10MB의 문자열
this.string4 = 'East';
}
}
// 인스턴스를 생성합니다.
const stringMemorySnapshot1 = new StringMemorySnapshot();
const stringMemorySnapshot2 = new StringMemorySnapshot();
위 코드를 크롬 개발자 도구의 메모리 탭에서 힙 스냅샷을 찍어보면, string1과 string2는 같은 값이지만 각각 다른 메모리 주소를 갖습니다. 반면, string3과 string4는 서로 다른 인스턴스에서도 동일한 메모리 주소를 공유합니다.
StringMemorySnapshot1
string1
::"aaa ..."@104733string2
::"aaa ..."@104735string3
::"bbb ..."@55971string4
::"East"@56007
StringMemorySnapshot2
string1
::"aaa ..."@104829string2
::"aaa ..."@104831string3
::"bbb ..."@ 55971string4
::"East"@ 56007

또한, string1
과 string2
는 메모리 탭에서 (concatenated string)
이라는 공간에서 여러 개의 메모리 주소로 분할되어 있는 것을 볼 수 있습니다. 이는 V8이 문자열을 효율적으로 관리하기 위한 내부 최적화 때문입니다.

문자열의 내부 관리 방식
문자열 처리 방식은 최적화 방식에 따라 더 다양하게 나뉠 수 있겠지만 임의로 정적인 문자열과 동적인 문자열 이라는 큰 틀로 나눠서 살펴보겠습니다.
(V8/string.cc 코드를 기반으로 chat gpt를 통해 분석하였습니다.)
1. 정적인 문자열 (Static Strings)
정적인 문자열은 코드 내에서 문자열 리터럴(String Literals)로 직접 생성되는 문자열을 말합니다. 예를 들어, string3
과 string4
가 이에 해당합니다.
- 특징 및 내부 처리 방식:
- 문자열 내부화(Internalization): V8 엔진은 정적인 문자열을 효율적으로 관리하기 위해 문자열 내부화 기법을 사용합니다. 동일한 내용의 문자열은 같은 메모리 주소를 참조하게 되어, 중복되는 문자열의 메모리 사용을 최소화합니다.
- 메모리 효율성: 동일한 문자열 내용이 여러 번 사용될 때, 새로운 메모리를 할당하지 않고 기존 메모리 주소를 재사용해 메모리 사용량을 줄입니다.
- 빠른 비교: 내부화된 문자열은 메모리 주소를 비교함으로써 문자열의 동일성을 빠르게 확인할 수 있습니다.
2. 동적인 문자열 (Dynamic Strings)
동적인 문자열은 문자열 메서드(String Methods)나 연산(Operation)을 통해 생성되는 문자열을 말합니다. string1
과 string2
가 이에 해당합니다.
- 특징 및 내부 처리 방식:
- ConsString 구조: 동적인 문자열은 ConsString이라는 구조로 관리됩니다. ConsString은 두 개의 하위 문자열을 참조하여 하나의 큰 문자열을 만들며, 메모리 할당과 복사를 최소화하기 위해 트리 형태로 구성됩니다.
- 메모리 할당 기준: ConsString은 문자열 크기보다는 연결 연산의 빈도와 패턴에 따라 사용되며, 이를 통해 문자열 연결 시 메모리 효율성을 높입니다.
- 성능 최적화: ConsString을 통해 문자열 연결이나 반복 연산을 최적화할 수 있습니다. 필요할 때는 ConsString 트리를 평탄화하여 단일 문자열로 변환함으로써 성능을 향상시킵니다. 이를 위해 V8 엔진은
String::SlowFlatten
,String::WriteToFlat
,ConsStringIterator
등의 메서드를 사용합니다.
결론
이번 글에서는 문자열(String)에 대해 V8 엔진이 어떻게 메모리를 관리하고 최적화하는지 살펴보았습니다. 역시 관련된 공식 내용을 찾기 힘들어서 크롬 개발자도구를 통한 실제 메모리 영역 테스트와 V8 구현 코드를 기반으로 글을 작성하였습니다.
임의로 정적인 문자열과 동적인 문자열 이라는 기준을 뒀으며, 잘못된 정보가 있거나 궁금한 점이 있을 경우 댓글 부탁드립니다.
원시 타입과 참조 타입 시리즈
- V8 엔진의 숫자 처리 방식
- V8 엔진의 문자열 처리 메커니즘 (본 게시글)
- V8 엔진의 Oddball 타입
- V8 엔진의 참조 타입
'Javascript' 카테고리의 다른 글
자바스크립트의 this 알아보기 (2) | 2024.11.09 |
---|---|
원시 타입과 참조 타입 - 4. V8엔진의 참조 타입 (2) | 2024.10.23 |
원시 타입과 참조 타입 - 3.V8 엔진의 Oddball 타입 (0) | 2024.10.21 |
원시 타입과 참조 타입 - 1. V8 엔진의 숫자 처리 방식 (0) | 2024.10.17 |