백엔드

메모리 누수: Memory Leak

Newbie Developer 2025. 2. 23. 22:44

 

메모리 누수(Memory Leak)란 프로그램이 동적으로 할당한 메모리를 해제하지 않아서, 사용하지 않는 메모리가 계속해서 점유되는 현상을 의미합니다. 메모리 누수가 발생하면 시스템의 가용 메모리가 줄어들고, 성능 저하 및 심각한 경우 프로그램이 크래시할 수도 있습니다.


1. 메모리 누수의 원인

메모리 누수는 주로 다음과 같은 이유로 발생합니다.

(1) 동적 메모리 할당 후 해제하지 않는 경우

C, C++ 같은 언어에서는 malloc(), new 등을 사용하여 동적으로 할당한 메모리를 free(), delete로 해제해야 합니다. 그러나 개발자가 실수로 해제하지 않으면 메모리 누수가 발생합니다.

void memoryLeak() {
    int* ptr = new int[10];  // 동적 메모리 할당
    // delete[] ptr; // 해제를 하지 않으면 메모리 누수 발생
}

(2) 참조가 끊긴 객체를 가비지 컬렉터(GC)가 수집하지 못하는 경우

Java, Python, Go 같은 언어에서는 가비지 컬렉터(GC)가 메모리를 자동으로 관리하지만, 강한 참조를 유지한 채 해제하지 않으면 메모리 누수가 발생할 수 있습니다.

import java.util.ArrayList;
import java.util.List;

class MemoryLeakExample {
    private static List<byte[]> memoryLeakList = new ArrayList<>();

    public static void main(String[] args) {
        for (int i = 0; i < 10000; i++) {
            memoryLeakList.add(new byte[1024 * 1024]); // 1MB 할당
        }
    }
}

위 코드에서는 memoryLeakList가 계속해서 새로운 데이터를 추가하면서도 해제되지 않기 때문에, 프로그램 종료 전까지 메모리 누수가 발생합니다.

(3) 이벤트 리스너나 콜백 미해제

이벤트 리스너를 등록하고 해제하지 않으면 객체가 해제되지 않고 계속 메모리를 점유할 수 있습니다.

window.addEventListener("scroll", () => {
    console.log("Scrolling...");
});
// removeEventListener("scroll", handler); // 미해제 시 메모리 누수 발생 가능

(4) 순환 참조 (JavaScript, Python 등)

JavaScript나 Python에서 객체 간 순환 참조가 발생하면 가비지 컬렉터가 해당 객체를 수집하지 못해 메모리 누수가 발생할 수 있습니다.

class A:
    def __init__(self):
        self.b = None

class B:
    def __init__(self):
        self.a = None

a = A()
b = B()
a.b = b
b.a = a

# del a, del b 하더라도 순환 참조 때문에 가비지 컬렉터가 제거하지 못할 수 있음

2. 메모리 누수의 영향

메모리 누수가 지속되면 다음과 같은 문제가 발생할 수 있습니다.

  1. 점진적인 메모리 부족: 시스템의 가용 메모리가 줄어들어 성능이 저하됩니다.
  2. OOM(Out of Memory) 발생: 메모리가 부족해 프로그램이 강제 종료될 수 있습니다.
  3. 성능 저하: 메모리 부족으로 인해 스왑 사용이 증가하면서 응답 속도가 느려질 수 있습니다.
  4. 가비지 컬렉션 부담 증가: GC가 더 자주 실행되면서 CPU 성능이 저하될 수 있습니다.

3. 메모리 누수 탐지 방법

메모리 누수를 감지하려면 다양한 도구를 사용할 수 있습니다.

언어 도구
C/C++ Valgrind, AddressSanitizer
Java VisualVM, YourKit, Eclipse MAT
JavaScript Chrome DevTools (Performance, Memory 탭)
Python objgraph, memory_profiler, tracemalloc
Go pprof

예시) Python에서 메모리 누수 탐지

import tracemalloc

tracemalloc.start()
# 메모리 사용 코드 실행
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')

for stat in top_stats[:10]:  # 메모리를 많이 사용하는 상위 10개 코드 출력
    print(stat)

4. 메모리 누수 해결 방법

메모리 누수를 방지하려면 다음과 같은 방법을 사용해야 합니다.

(1) C/C++에서 동적 할당 후 해제

int* ptr = new int[10];
delete[] ptr;  // 메모리 해제

(2) Java에서 객체 참조 해제

object = null;  // 명시적으로 참조 해제

또는 WeakReference를 사용하여 가비지 컬렉션이 가능하도록 함.

(3) 이벤트 리스너 해제 (JavaScript)

element.removeEventListener("click", eventHandler);

(4) Python에서 순환 참조 방지

import weakref
class A:
    def __init__(self):
        self.b = weakref.ref(B())  # 약한 참조 사용

(5) GC 강제 실행 (Java, Python)

System.gc(); // Java
import gc
gc.collect()  # Python

하지만 GC 강제 실행은 성능 저하를 유발할 수 있으므로 주의해서 사용해야 합니다.


5. 결론

메모리 누수는 장기적으로 프로그램의 성능을 저하시킬 수 있는 치명적인 문제입니다. 특히 서버 애플리케이션이나 장시간 실행되는 프로그램에서는 메모리 누수를 지속적으로 감시하고, 적절한 도구를 활용하여 문제를 예방하는 것이 중요합니다.

✔️ 핵심 요약

  • C/C++: 동적 할당 후 반드시 해제 (malloc/free, new/delete)
  • Java/Python: 순환 참조 및 이벤트 리스너 관리
  • JavaScript: removeEventListener() 사용
  • 메모리 프로파일링 도구 활용: Valgrind, VisualVM, pprof 등으로 지속적인 모니터링

🚀 안정적인 프로그램 운영을 위해 메모리 관리에 신경 씁시다!