운영체제

힙 메모리의 단편화 fragmentation 발생 이유와 해결방법

2025-07-20 11:45


힙 메모리의 단편화 fragmentation 발생 이유와 해결방법

시작하기

개발을 하다보면, 메모리 누수, GC, 힙 메모리 등의 용어를 들어본 적이 있을 것이다. 단순히 메모리가 부족하거나 프로그램이 무거우면 느려진다고 생각할 수 있겠지만, 사실 힙(Heap) 메모리 영역의 단편화 문제 때문에 발생할 수 있다. 힙 메모리가 단편화 문제가 왜 발생하고 어떻게 GC가 이 문제를 해결하고 있는지 작동 방식에 대해 알아보자.

단편화(fragmentation)란 왜 발생하는 것일까?

힙 메모리 구조

운영체제가 프로그램을 실행할때 힙 영역은 런타임에 필요한 크기의 메모리를 동적으로 할당하고 해제하는 메모리를 위한 공간이다. 개발자가 malloc(C 언어) 또는 new(c++, Java 등)과 같은 함수를 사용해서 메모리를 요청하면 힙에서 공간이 할당된다. 자바스크립트에서는 객체를 생성하거나 클로저를 만들면 대부분 Heap에 저장된다. 수동 또는 자동으로 메모리 해제가 필요하고, 개발자가 명시적으로 해제하지 않으면 메모리 누수(Memory leak)가 발생할 수 있다.

참고로, 스택 영역은 함수 호출과 지역 변수를 저장하는 영역이고, 자동으로 관리된다.

단편화(fragmentation) 문제

프로그램이 실행되는 동안, 다양한 크기의 메모리 블록이 필요에 따라 할당되고 해제되는 과정을 거친다. 예를 들어 웹 브라우저는 페이지를 로드할 때마다 이미지를 위한 메모리, 텍스트를 위한 메모리 등 다양한 크기의 공간을 요청하고, 페이지를 닫거나 다른 페이지로 이동했을때 이 공간들을 해제한다. 이렇게 메모리 공간이 할당되고 해제될 때, 사용 가능한 빈 공간이 조각 조각 흩어져 할당이 실패하게 되는 메모리 단편화가 발생된다.

메모리 단편화 과정

단편화란 크게 외부 단편화와 내부 단편화로 나뉜다.

외부 단편화 (External Fragmentation)

내부 단편화 (Internal Fragmentation)

메모리 단편화는 동적 메모리 할당의 결과물로 메모리 자원의 효율성을 떨어뜨리고 때로는 충분한 메모리가 있음에도 불구하고 프로그램이 더 이상 메모리를 할당받지 못하게 만드는 원인이 된다

단편화를 해결하는 전략들

운영체제나 런타임 환경에서는 이러한 단편화 문제를 줄이기 위한 여러 전략들을 사용한다.

1. 메모리 풀 (Memory Pool)

2. 버디 시스템 (Buddy Allocation)

버디 시스템은 메모리 할당 요청을 2의 거듭제곱 단위로 메모리를 나누고 병합해서 관리하는 방식이다.

  1. 전체 메모리 공간을 2의 거듭제곱 크기의 가장 큰 블록으로 시작한다.(예.128KB)
  2. 메모리 요청이 들어오면 해당 요청을 만족시킬 수 있는 최소 2의 거듭제곱 크기의 블록을 찾는다
  3. 만약 적합한 블록이 없다면, 현재 사용 가능한 큰 블록을 동일한 크기의 두 버디(Buddy) 블록으로 나눈고 이 과정은 요청된 크기보다 작아질 때까지 재귀적으로 반복된다
  4. 메모리 해제 시에는 해제된 블록의 버디 블록이 비어있으면, 두 버디 블록을 합쳐서 더 큰 블록으로 만든다(병합). 이 과정도 재귀적으로 반복될 수 있다.

3. GC + Compaction (가비지 컬렉션 + 압축)

  1. Mark(표시) - 가비지 컬렉터가 메모리 힙을 스캔해서 현재 사용 중인(reachable)객체들을 표시한다.
  2. Sweep (스윕) - 더 이상 사용되지 않는 객체들의 메모리를 해제한다
  3. Compaction (압축) - 해제된 공간들을 제거하고, 사용 중인 객체들을 메모리의 한쪽 끝으로 밀어 넣어서 연속적인 하나의 큰 빈 공간을 만든다.

장점

단점

V8 엔진의 Scavenger
자바스크립트 엔진인 V8 엔진은 Scavenger로 가비지 컬렉션(GC) 역할을 한다. 주로 새로 생성된(young) 객체가 저장되는 신생 영역(New Space)을 관리한다. Minor GC 라고도 부른다.

  • Cheney’s Algorithm이라는 복사(Copying) GC 알고리즘을 기반으로 동작하며, New Space를 To-Space와 From-Space라는 두 개의 세미 스페이스(semi-space)로 나눈다
  • Scavenger는 신생 영역의 객체들을 자주 빠르게 정리해서 GC 작업의 효율성을 크게 높인다
  • 메모리 영역의 크기가 작고 살아있는 객체만 옮기는 복사GC을 사용하기 때문에 Stop-The-World 시간이 매우 짧다 (보통 1ms 미만)
  • 객체를 복사하는 과정에서 메모리가 자동으로 압축되기 때문에 신생 영역에서는 외부 단편화가 발생하지 않는다

JS와 브라우저 메모리 모델

브라우저에서 메모리 누수가 발생할 수 있는 경우가 어떤 경우일까?

전역 변수 및 암시적 전역변수 오남용할 경우

function pingServer() {
  setTimeout(() => {
    console.log('서버에 핑 보내기');
    pingServer(); // 재귀 호출
  }, 3000);
}

이벤트 리스너를 해제하지 않을 경우

function addListener() {
  const button = document.getElementById('myButton');

  button.addEventListener('click', function handleClick() {
    console.log('clicked!');
  });
}

클로저 (Closures)의 잘못된 사용

function setup() {
  const largeData = new Array(1000000).fill('😵');

  document
    .getElementById('myBtn')
    .addEventListener('click', function handleClick() {
      console.log(largeData[0]); // 클로저로 largeData를 계속 참조함
    });
}

요약하기




 운영체제  단편화  힙메모리  GC  메모리누수  Computer Science