본문 바로가기

스택과 힙 메모리: 프로그램 데이터가 저장되는 두 공간의 차이

📑 목차

    스택과 힙이 헷갈리는 실제 상황

    스택과 힙 메모리는 프로그램 데이터가 저장되는 두 공간의 차이를, 오류 메시지가 튀거나 속도가 갑자기 떨어지는 상황에서 어떤 순서로 구분할지 정리한다.

    재귀 호출을 조금만 늘렸는데 갑자기 종료되거나, 오래 켜 둔 서버가 점점 느려지다가 메모리 부족으로 죽는 문제가 대표적이다.

    둘 다 "메모리가 부족해서"로 보이지만, 원인과 대응은 완전히 다르다.

    스택은 보통 "지금 실행 중인 흐름"을 담는 공간이고, 힙은 "오래 살아남는 데이터"를 담는 공간이라는 관점으로 보면 판단이 빨라진다.

    핵심 개념 1: 스택 메모리가 담당하는 것

    스택(Stack) 메모리는 함수 호출이 쌓이는 순서대로 프레임(frame)을 만들고, 지역 변수와 반환 주소 같은 실행 정보를 짧은 주기로 저장한다.

    핵심 특징은 "자동 할당·자동 해제"다.

    함수가 시작될 때 스택 프레임이 생기고, 함수가 끝나면 그 프레임이 한 번에 정리된다.

    주의점은 스택 크기가 제한되어 있다는 점이다.

    너무 깊은 재귀, 큰 지역 배열, 과도한 중첩 호출은 스택을 빠르게 소진한다.

    함수 호출 흐름과 동적 데이터를 저장하는 방식 대비
    스택과 힙 메모리의 저장 방식 대비

    핵심 개념 2: 힙 메모리가 담당하는 것

    힙(Heap) 메모리는 런타임이 요청에 따라 동적으로 데이터를 잡아두는 공간이며, 객체·버퍼·컬렉션처럼 크기와 수명이 다양한 데이터를 담는다.

    핵심 특징은 "수명 제어 방식이 언어/런타임마다 다르다"는 점이다.

    C/C++처럼 직접 해제하는 모델이 있고, Java/JavaScript처럼 GC가 회수하는 모델이 있다.

    주의점은 힙은 "자동으로 늘어나기만 하는 공간"이 아니라는 점이다.

    해제를 잊거나 참조가 남아 있으면 회수되지 않으며, 조각화(fragmentation)나 GC 압박 때문에 지연이 커질 수 있다.

    스택과 힙 차이 비교표와 빠른 판단 체크

    스택과 힙 메모리의 차이는 "저장 대상", "수명", "관리 방식", "실패 형태"에서 가장 빠르게 갈린다.

    항목 설명
    저장되는 데이터 스택: 함수 호출 흐름(스택 프레임), 지역 변수 / 힙: 객체, 버퍼, 컬렉션 등 동적 데이터
    수명 스택: 함수가 끝나면 즉시 소멸 / 힙: 참조가 남아 있는 동안 유지(또는 명시 해제 시까지)
    할당·해제 스택: 빠르고 규칙적(푸시/팝) / 힙: 상대적으로 비용이 크고 런타임 정책 영향을 받음
    대표 증상 스택: 호출 깊이 증가 시 즉시 크래시 / 힙: 시간 경과에 따라 사용량 증가, GC 지연, OOM
    대표 오류 메시지 스택: call stack size exceeded, stack overflow / 힙: heap out of memory, out of memory

    아래 YES/NO는 "지금 문제는 스택 쪽인가, 힙 쪽인가"를 빠르게 가르는 질문이다.

    • YES/NO: 재귀/중첩 호출을 조금 늘리면 바로 종료되는가?
    • YES/NO: 에러가 "call stack" 또는 "stack overflow"를 직접 언급하는가?
    • YES/NO: 실행 시간이 길어질수록 메모리 사용량이 꾸준히 상승하는가?
    • YES/NO: GC가 있는 런타임에서 멈칫거림(일시 정지)이 자주 생기는가?
    • YES/NO: OOM 직전에 응답 지연이 증가하고, 처리량이 떨어지는가?

    어디서 어떻게 확인하는가: 점검 절차

    스택과 힙 메모리 문제는 "메시지 확인 - 호출 경로 확인 - 힙 스냅숏 확인" 순서로 보면 재현성이 높다.

    1. 브라우저(프런트): Chrome DevTools - Console에서 오류 메시지에 "call stack" 또는 "out of memory"가 있는지 확인한다.
    2. 브라우저(프런트): Chrome DevTools - Performance에서 Main 스레드의 긴 작업과 함수 호출 폭을 확인한다(체크 포인트: 긴 스크립트 실행, 반복 호출).
    3. 브라우저(프런트): Chrome DevTools - Memory에서 Heap snapshot을 2-3회 비교한다(체크 포인트: 특정 객체 타입이 계속 증가, Detached DOM 같은 잔존 참조).
    4. Node/서버: node 실행 옵션에 --inspect를 붙이고 Chrome에서 chrome://inspect로 접속해 Memory 탭을 연다(체크 포인트: heapUsed 상승 추세, 스냅숏 비교).
    5. 공통: 스택 의심이면 호출 깊이를 줄이는 변경(재귀 제거, 반복으로 전환, 큰 지역 변수 축소)을 먼저 적용한다.
    6. 공통: 힙 의심이면 "참조가 남는 경로"를 끊는 변경(캐시 상한, 이벤트 리스너 해제, 전역 컬렉션 정리)을 적용하고 변화량을 재측정한다.

    실전 예시는 다음처럼 시작되는 경우가 많다.

    간단한 재귀 함수가 특정 입력에서만 터지고, 콘솔에는 호출 스택 관련 문구가 찍힌다.

    예시 관측(민감정보 제거)
    RangeError: Maximum call stack size exceeded
    depth=15342 heapUsedMB=86

    이 경우 선택은 "스택 사용을 줄이는 방향"이 우선이다.

    재귀를 반복문으로 바꾸거나, 한 번에 처리하던 작업을 쪼개 호출 깊이를 제한하면 즉시 안정화되는 경우가 많다.

    반대로 같은 코드에서 전역 배열이나 캐시에 객체를 계속 쌓아두면 힙이 늘어나며, 시간이 지나서 "heap out of memory" 형태로 재등장할 수 있다.

    스택과 힙 메모리 문제를 구분하는 점검 절차
    스택 vs 힙 문제를 구분하는 점검 흐름

    결론

    스택과 힙 메모리는 저장 대상과 수명, 관리 방식이 달라서 같은 "메모리 문제"처럼 보여도 접근이 달라진다.

    스택 문제는 호출 깊이와 큰 지역 변수에서 빠르게 터지며, 구조를 바꾸면 즉시 효과가 나는 경우가 많다.

    힙 문제는 참조가 남는 경로와 누적이 핵심이라, 스냅숏 비교와 상한 설정이 가장 먼저 먹히는 편이다.

    연계 주제로는 가비지 컬렉션(GC) 동작 원리, 메모리 누수(leak) 패턴과 탐지 방법이 이어진다.

    FAQ

    Q1. 스택과 힙 메모리 중 어느 쪽이 더 빠른가

    일반적으로 스택은 푸시/팝이 규칙적이라 할당·해제 비용이 작다.

    힙은 동적 관리가 들어가며 런타임 정책과 조각화, GC 영향으로 비용이 커질 수 있다.

    Q2. "call stack size exceeded"가 뜨면 힙을 늘리면 해결되는가

    대부분 해결되지 않는다.

    이 메시지는 스택 프레임이 과도하게 쌓인 상황이므로 호출 깊이를 줄이거나 구조를 바꾸는 방식이 맞다.

    Q3. 힙 메모리 문제는 왜 시간이 지나야 드러나는가

    힙은 객체가 누적되거나 참조가 남아 회수되지 않을 때 서서히 증가하는 경우가 많다.

    그래서 실행 직후보다 장시간 운영에서 GC 지연, 사용량 상승, OOM 형태로 늦게 나타나기 쉽다.