Krafton Jungle/2. Keywords

[WEEK06] 메모리 누수

munsik22 2025. 4. 18. 17:47

메모리 누수

메모리 누수memory leak란, 프로그램이 필요하지 않은 메모리를 계속 사용하는 현상을 말한다. 할당된 메모리를 사용한 후 반환하지 않으면 메모리가 낭비되고, 결국 메모리 부족으로 인한 오류가 발생할 수 있다.

 

할당된 메모리를 사용한 다음 반환하지 않는 것이 누적되면 메모리가 낭비된다. 즉, 더 이상 불필요한 메모리가 해제되지 않으면서 메모리 할당을 잘못 관리할 때 발생한다. 이로 인해 사용 가능한 메모리가 점점 줄어들며, 장기적으로는 시스템 성능 저하, 응용 프로그램의 비정상 종료 또는 전체 시스템의 불안정성을 초래할 수 있다.

원인
이전에 할당된 메모리를 올바르게 해제하지 못함
증상
프로그램 성능 저하, Out of Memory Error, 프로그램 응답하지 않음, 강제 종료
해결 방법
힙 덤프 분석을 통해 문제 지점을 찾고 해결
방지 방법
해제되지 않은 리소스를 정상적으로 해제하고, static 변수가 큰 객체를 참조하지 않도록 주의

메모리 누수의 원인

메모리 누수는 주로 다음과 같은 상황에서 발생한다.

  • 할당 후 해제하지 않음: malloc, calloc, realloc 등을 통해 메모리를 할당하고, free로 해제하지 않는 경우
  • 포인터를 잃어버림: 할당받은 메모리를 가리키는 포인터가 다른 값을 참조하게 되어 기존에 할당된 메모리에 접근할 수 없게 되는 경우
  • 반복적인 할당: 루프 내에서 반복적으로 메모리를 할당하고, 매번 해제하지 않는 경우
  • 예외 처리 누락: 조건 분기나 오류 발생 시 free 호출 없이 함수가 종료되는 경우

C 언어에서의 메모리 누수 예시

1. 포인터를 덮어씌워서 원래 주소를 잃어버리는 경우

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int *)malloc(sizeof(int) * 5);

    if (ptr == NULL) return 1;

    ptr = (int *)malloc(sizeof(int) * 10);  // 이전에 할당한 5개짜리 메모리 주소를 잃어버림

    free(ptr);  // 두 번째 할당된 것만 해제됨, 첫 번째는 누수 발생

    return 0;
}

2. 조건문/에러 분기 등에서 free 누락

#include <stdio.h>
#include <stdlib.h>

int process(int *arr) {
    if (arr == NULL) {
        return -1;
    }

    // 중간에 오류가 발생했다고 가정
    if (1) {
        return -1;  // 메모리 해제 없이 조기 종료 → 누수 발생
    }

    free(arr);
    return 0;
}

int main() {
    int *data = (int *)malloc(sizeof(int) * 100);
    process(data);  // 누수 발생 가능
    return 0;
}

3. 루프에서 할당만 하고 해제하지 않는 경우

#include <stdio.h>
#include <stdlib.h>

int main() {
    for (int i = 0; i < 100; i++) {
        int *arr = (int *)malloc(sizeof(int) * 50);
        // free(arr); // 주석 처리로 인해 반복적으로 메모리 누수 발생
    }
    return 0;
}

4. 구조체 내부 포인터 누수

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    char *name;
} Person;

int main() {
    Person *p = (Person *)malloc(sizeof(Person));
    p->name = (char *)malloc(50);

    // 일부 메모리만 해제
    free(p);  // p->name은 해제되지 않음 → 누수 발생

    return 0;
}
  • 구조체 멤버도 별도로 free(p->name); 하고 나서 free(p); 해야 완전한 해제가 된다.

5. 전역 또는 정적 변수에 할당 후 해제 안 함

#include <stdlib.h>

char *global_ptr;

void allocate() {
    global_ptr = (char *)malloc(100);
    // 프로그램 종료 전까지 해제하지 않으면 누수로 간주될 수 있음
}

int main() {
    allocate();
    return 0;
}
  • 이 경우는 프로그램 종료 시 OS가 회수하긴 하지만, 장시간 실행되는 프로그램에서는 실제 누수로 이어질 수 있다.

메모리 누수를 방지하는 방법

  • 사용 후 반드시 free 호출: 동적으로 할당한 메모리는 사용이 끝나면 반드시 해제해야 한다.
  • 포인터 관리 철저: 포인터를 덮어쓰기 전에 먼저 기존 메모리를 free한다.
  • 메모리 추적 도구 사용: valgrind등의 도구를 활용하여 누수를 탐지하고 디버깅한다.
  • 일관된 메모리 관리 정책: 할당과 해제를 동일한 함수 또는 모듈 내에서 관리하면 실수를 줄일 수 있다.
  • 코딩 스타일 지키기: 예를 들어, 할당과 해제를 짝지어 사용하는 코드를 작성하거나, 예외 상황에서도 반드시 해제를 수행하도록 한다.

Valgrind 사용하기

// valgrind 설치 (ubuntu)
sudo apt update
sudo apt install valgrind

// hello 프로그램의 메모리 누수 확인
valgrind --leak-check=full ./hello

Valgrind가 출력하는 메시지는 대략 다음과 같다.

==12345== Memcheck, a memory error detector
==12345== LEAK SUMMARY:
==12345==    definitely lost: 40 bytes in 1 blocks
==12345==    indirectly lost: 0 bytes in 0 blocks
==12345==      possibly lost: 0 bytes in 0 blocks
==12345==    still reachable: 0 bytes in 0 blocks
==12345==         suppressed: 0 bytes in 0 blocks
==12345== 
==12345== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==12345==    at 0x........: malloc (vg_replace_malloc.c:xxx)
==12345==    by 0x........: main (hello.c:4)

 

  • definitely lost: 확실한 메모리 누수! free되지 않고 포인터도 잃어버린 메모리
  • still reachable: main 끝까지 포인터가 남아 있지만 free 안 한 경우 (일부 상황에선 괜찮지만 일반적으로 정리하는 것이 좋다)
  • possibly lost: 포인터가 의심스러운 방식으로 덮어쓰여 추적이 어려운 메모리

 

Valgrind에서 사용할 수 있는 유용한 옵션들이 있다.

옵션 설명
--leak-check=full 누수 상세 정보 출력
--track-origins=yes 오류의 원인 추적 가능 (느리지만 유용)
--show-leak-kinds=all 모든 종류의 누수 보여줌
--log-file=filename.txt 출력 결과를 파일로 저장

 

Valgrind는 메모리 누수 뿐만 아니라 Segfault가 어디에서 발생했는지 확인할 수도 있다.


메모리 누수는 C 언어와 같이 수동 메모리 관리가 필요한 언어에서 자주 발생하는 문제다. 특히 시스템 자원이 제한적인 임베디드 시스템이나 실시간 시스템에서는 더욱 치명적일 수 있다. 따라서 올바른 메모리 관리 습관과 도구의 활용은 안정적인 소프트웨어 개발에 필수적이라고 할 수 있겠다.

'Krafton Jungle > 2. Keywords' 카테고리의 다른 글

[WEEK07] 동적 메모리 할당  (0) 2025.04.25
[WEEK06] AVL 트리  (0) 2025.04.21
[WEEK06] 레드 블랙 트리 (Red-Black Tree)  (0) 2025.04.17
[WEEK06] 균형 탐색 트리  (0) 2025.04.16
[WEEK05] KMP법, 보이어-무어법  (0) 2025.04.14