Item 7. 다 쓴 객체 참조를 해제하라

메모리 누수(Memory Leak)

  • C, C++과 같은 low level 언어와 달리 Java는 가비지 컬렉터가 참조되지 않는 객체를 알아서 회수(메모리에서 제거)하기 때문에 프로그래머가 명시적으로 객체를 방출할 필요가 없음

  • 하지만, 실제로 앞으로 사용될 일이 없음에도 가비지 컬렉터가 회수하지 않는 객체들이 존재하게 될 수 있고, 이를 메모리 누수 라고 함

  • 이는 논리적으로 호출될 일이 없음에도 해당 객체들의 참조를 여전히 가지고 있기 때문에 발생하는 문제

  • 이러한 사례를 예방하기 위해서는 다 쓴 참조를 null처리할 필요가 있음

    • 하지만 매 객체를 미사용 순간마다 null로 처리하는 일은 코드를 필요 이상으로 지저분하게 만듦

    • 그렇다면 어떨 때 명시적 null 처리를 수행해야 할까?

메모리 누수 사례와 해결 방법

자기 자신의 메모리를 직접 관리하는 클래스

  • Stack, Queue와 같은 Data Structure의 구현체가 이에 해당

  • 이 경우 원소를 제거할 때(pop, deque 등) 명시적으로 해당 객체에 대한 자료구조 클래스의 참조를 null처리해 주는 것이 좋음

캐시

  • In-memory 캐시에 저장한 뒤, 해당 캐시가 사용되지 않음에도 해당 캐시에 대한 참조가 존재하면 이또한 메모리 누수에 해당

  • 캐시 키에 대한 참조가 유지되는 동안 캐시가 유효하도록 구현할 수 있다면, WeakHashMap을 사용함으로써 이러한 누수를 예방할 수 있음

    • WeakHashMap이란, key에 대한 참조가 사라지면 해당 entry를 제거하는 Map 구현체

  • 아니면 백그라운드에서 동작하는 스레드를 통해 캐시 사용 여부를 체크하거나, 직접 방출 알고리즘을 적용할 수도 있음

    • 예시: LinkedHashMap으로 구현한 후 새 데이터가 추가될 때 지정된 MAX_SIZE를 넘었다면 removeEldestEntry를 통해 가장 오래된 데이터를 방출 (LRU)

리스너(Listener), 콜백(Callback)

  • 리스너, 콜백은 클라이언트가 등록 후 명시적으로 해지해야 메모리에서 방출됨

  • 즉, 별도 조치가 없으면 메모리에 계속 쌓이게 됨 (메모리 누수)

  • 이를 예방하기 위해서는 이들을 약한 참조(weak reference)로 저장해야 함

    • 이를 위해 WeakHashMap을 사용할 수 있음

가장 좋은 메모리 관리 방법

  • 명시적 null처리를 하기보다는, 변수를 최소한의 scope에 포함되도록 정의해 scope가 끝날 때 해당 scope 내에서 사용된 변수들이 가비지 컬렉터에 의해 자동으로 회수되도록 하는 것

Reference

  • https://blog.breakingthat.com/2018/08/26/java-collection-map-weakhashmap/

Last updated