본문 바로가기
카테고리 없음

[기획 연재 12편] 메모리 누수(Memory Leak): 닫지 않은 수도꼭지가 시스템을 망가뜨리는 과정

by khhjyc_ 2026. 1. 18.

서론: 우주에서 재부팅은 목숨을 건 도박이다

여러분의 PC가 느려지면 어떻게 하시나요? 그냥 재부팅하면 해결됩니다. 하지만 화성으로 가는 우주선이나 365일 내내 켜져 있어야 하는 은행 서버는 마음대로 껐다 켤 수 없습니다.

제가 참여했던 장기 우주 프로젝트에서 가장 경계했던 적은 거창한 버그가 아니었습니다. 바로 하루에 1바이트씩 아주 조금씩 사라지는 메모리, 즉 '메모리 누수(Memory Leak)'였습니다. 처음엔 티도 안 나지만, 몇 달이 지나면 시스템의 메모리가 바닥나고 결국 먹통이 되어버리는 침묵의 살인자입니다.

오늘 12편에서는 C언어 개발자가 반드시 책임져야 할 '빌린 것을 돌려주는 의무'에 대해 이야기해 보겠습니다.

본론 1: 도서관 책을 반납하지 않는 사람들

지난 4편에서 배운 '힙(Heap)' 영역을 기억하시나요? 이곳은 개발자가 필요할 때 메모리를 빌려 쓰는(동적 할당) 자유 구역입니다.

C언어에서는 malloc() 함수로 메모리를 빌립니다. 이것은 도서관에서 책을 대출하는 것과 같습니다. 책을 다 읽었으면 free() 함수를 써서 반드시 반납해야 합니다.

  • 정상적인 코드: 대출(malloc) → 사용 → 반납(free). 다른 사람이 그 책(메모리)을 다시 빌릴 수 있습니다.
  • 메모리 누수: 대출(malloc) → 사용 → 반납 안 함(free 누락).

문제는 컴퓨터가 "아, 이 사람 책 다 읽었네?"라고 판단해서 알아서 가져가지 않는다는 점입니다(C언어의 특징). 개발자가 반납하지 않은 메모리는 프로그램이 종료될 때까지 '사용 중(In Use)'인 상태로 남아, 누구도 쓸 수 없는 쓰레기 공간이 되어버립니다.

본론 2: 시스템은 어떻게 죽어가는가? (OOM)

메모리 누수가 발생하면 초기에는 아무런 증상도 없습니다. 하지만 프로그램이 오래 실행될수록 힙 메모리에는 반납되지 않은 쓰레기가 쌓여갑니다.

  1. 성능 저하: 가용 메모리가 줄어들면 운영체제는 하드디스크를 임시 메모리로 쓰는 '스왑(Swap)'을 시도합니다. 이때 컴퓨터가 급격히 느려집니다.
  2. 기능 마비: 새로운 데이터를 저장하려 해도 공간이 없어 저장이 실패합니다.
  3. 강제 종료 (OOM Killer): 결국 운영체제는 시스템 전체를 살리기 위해 메모리를 많이 잡아먹는 프로그램을 강제로 죽여버립니다. 이를 Out Of Memory(OOM) 오류라고 합니다.

본론 3: 보이지 않는 범인을 잡는 법

메모리 누수는 코드가 문법적으로는 완벽하기 때문에 컴파일러조차 찾아내지 못합니다. 그래서 더욱 위험합니다. NASA 엔지니어들은 이를 막기 위해 다음과 같은 원칙을 지킵니다.

1. 짝 맞추기 (Pairing)

코드를 짤 때 malloc을 썼다면, 그 즉시 코드 맨 아래에 free를 먼저 적어놓고 내용을 채웁니다. "여는 괄호가 있으면 닫는 괄호가 있어야 한다"는 것과 같은 이치입니다.

2. 전문 도구의 사용

사람의 눈으로는 찾기 힘들기 때문에 Valgrind(발그린드) 같은 메모리 분석 도구를 사용하여 프로그램을 검사합니다. 이 도구들은 프로그램이 끝난 뒤 반납되지 않은 메모리가 있는지 샅샅이 뒤져서 리포트를 해줍니다.

결론: C언어는 청소부가 없다

파이썬이나 자바 같은 현대 언어들은 '가비지 컬렉터(청소부)'가 있어서 개발자가 메모리 해제에 신경 쓸 필요가 없습니다. 하지만 C언어는 개발자를 전적으로 신뢰합니다. "네가 빌렸으니, 네가 가장 적절한 시기에 돌려줄 것이라 믿는다"는 철학이죠.

이 자유와 책임의 무게를 견디는 것이 C언어 개발자의 숙명입니다. 닫지 않은 수도꼭지 하나가 집안 전체를 물바다로 만들 수 있음을 항상 기억하십시오.

다음 [Part 13. 가비지 컬렉터(GC) vs 수동 관리] 편에서는, 그렇다면 왜 최신 언어들은 자동으로 메모리를 관리해 주는지, 그리고 자동 관리가 만능인지에 대해 C언어와 비교하며 심층 분석해 보겠습니다.