Nodejs에서 sql += "" 방식과 배열(push & join) 방식을 V8 엔진의 메모리 할당 및 GC 관점에서 비교
1. 메모리 할당 구조 비교
sql += "" (문자열 결합)
- 불변성(Immutability): 매 연산마다 새로운 문자열 객체가 생성됩니다.
- ConsString 트리: V8은 내부적으로 ConsString이라는 이진 트리 구조를 만듭니다. +=를 할 때마다 트리의 깊이가 깊어집니다.
- 복사 비용: 나중에 이 문자열을 DB로 보낼 때(Flattening), 깊게 쌓인 트리를 순회하며 하나의 커다란 연속된 메모리 공간으로 전체 복사가 일어납니다.
const parts = [] (배열 방식)
- 가변성(Mutability): 배열은 요소가 추가되어도 배열 객체 자체의 메모리 주소가 바뀌지 않습니다.
- 참조 저장: parts.push("string")은 문자열의 실제 데이터를 복사하는 게 아니라, 해당 문자열이 위치한 **메모리 주소(포인터)**만 배열에 차곡차곡 쌓는 방식입니다.
- 연속적 할당: 배열은 내부에 요소가 늘어날 것을 대비해 메모리 공간을 미리 넉넉하게 예약(Over-allocation)하므로 재할당 횟수가 훨씬 적습니다.
2. GC(가비지 컬렉터) 관점의 차이
| 비교 항목 | sql += "" (문자열 더하기) | parts.join(" ") (배열 조인) |
| 중간 객체 생성 | 매우 많음. 연산 횟수만큼 중간 단계의 문자열/ConsString 노드가 생성됨. | 거의 없음. 기존 문자열들의 포인터만 배열에 담음. |
| Scavenge GC 부하 | New Space에 단기 생존 객체가 범람하여 Minor GC가 빈번해짐. | 배열 객체 하나만 관리하면 되므로 GC 부하가 현저히 낮음. |
| 메모리 파편화 | 크기가 제각각인 문자열 객체들이 생성/삭제되며 파편화 유발. | 고정된 배열 크기 내에서 관리되므로 메모리 배치가 효율적임. |
| 최종 메모리 복사 | 트리 구조를 풀면서 거대한 단일 메모리를 새로 할당함. | join() 시점에 딱 한 번만 최종 문자열을 위한 메모리를 할당함. |
3. 코드 실행 흐름 시각화
문자열 더하기 방식 (sql +=)
- "SELECT" 생성
- "SELECT" + " * " = 새로운 객체 A 생성 (기존 "SELECT"는 쓰레기)
- 객체 A + " FROM users" = 새로운 객체 B 생성 (객체 A는 쓰레기)
- ... 무한 반복 (중간에 생성된 A, B 등은 모두 GC가 치워야 할 짐)
배열 방식 (push & join)
- [] 배열 생성
- "SELECT", " * ", " FROM users" 주소값을 배열에 기록 (데이터 복사 없음)
- join() 호출 시점에 딱 한 번: [주소1, 주소2, 주소3]을 순회하며 최종 결과물 하나만 생성.
4. 실무적인 결론
Node.js 환경에서 동적으로 아주 긴 쿼리를 생성해야 한다면 배열에 push 하고 마지막에 join(' ') 하는 것이 훨씬 "GC 친화적"입니다.
- 성능: 수천 번 이상의 결합이 일어날 경우 배열 방식이 압도적으로 빠릅니다.
- 예측 가능성: GC가 언제 터질지 모르는 불안 요소(Stop-the-world)를 줄여줍니다.
반응형
'IT관련 > Nodejs' 카테고리의 다른 글
| Clinic.js doctor 명령어로 HTML 리포트가 안 만들어질 때 해결 방법 (0) | 2026.03.20 |
|---|---|
| 🛑 Node.js에서 sql += "" 방식이 GC에 치명적인 이유 (0) | 2026.03.20 |
| [Node.js] 성능 최적화의 필수 도구, Clinic.js 완벽 가이드 (0) | 2026.03.18 |
| nodejs에서 mysql 연결시 timezone 셋팅. (0) | 2025.07.07 |