본문 바로가기
독서이야기/이펙티브 자바 3 edition

[아이템7] 다 쓴 객체 참조를 해제하라

by 사랑꾼이야 2022. 9. 3.
반응형

이 내용은 이펙티브 자바 Effective Java 3/E 를 읽으면서 정리한 내용을 포함하고 있습니다.

자바는 가비지 컬렉터를 통해서 다 쓴 객체를 회수해 갑니다. 하지만 이러한 장점으로 인하여 자칫 메모리 관리에 더 이상 신경쓰지 않아도 된다고 오해할 수 있습니다. 스택을 간단히 구현한 다음 코드를 보도록 하겠습니다.

스택 코드

// Can you spot the "memory leak"?
public class Stack {

    private Object[] elements;

    private int size = 0;

    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {
        this.elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e) {
        this.ensureCapacity();
        this.elements[size++] = e;
    }

    public Object pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }

        return this.elements[--size]; // 주목!!
    }

    /**
     * Ensure space for at least one more element,
     * roughly doubling the capacity each time the array needs to grow.
     */
    private void ensureCapacity() {
        if (this.elements.length == size) {
            this.elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }
}
  • 앞 코드에서 메모리 누수는 어디서 일어날까? 이 코드에서는 스택이 커졌다가 줄어들었을 때 스택에서 꺼내진 객체들을 가비지 컬렉터가 회수하지 않습니다.
  • 프로그램에서 객체들을 더 이상 사용하지 않더라도 가비지 컬렉터에서 회수하지 않게 됩니다.

다 쓴 참조

메모리 누수 는 가비지 컬렉션 활동량과 메모리 사용량이 늘어나게 되면 성능 저하로 인하여 발생할 수 있습니다. 심할 때는 디스크 페이징이나 OutOfMemoryError 를 일으켜 프로그램이 예기치 않게 종료될 수 있습니다.

다시 쓰지 않을 참조가 메모리로 반환되지 않고 계속 갖고 있으면서 메모리 누수가 발생할 수 있습니다.

가비지 컬렉션 언어에서는 메모리 누수를 찾기가 아주 까다롭습니다. 객체 하나를 살려주면 가비지 컬렉터는 그 객체뿐이 아니라 그 객체가 참조하는 모든 객체(그리고 또 그들이참조하는 객체)를 회수해가지 못합니다.

메모리를 직접 관리하는 클래스는 프로그래머가 메모리 누수를 조심해야 합니다.

해결된 스택 코드

public Object pop() {
    if (size == 0) {
        throw new EmptyStackException(); 
    }
    Object result = elements[--size];
    elements[size] = null; // 다 쓴 참조 해제
    return result;
}

다 쓴 참조를 해결하는 가장 간단한 방법은 참조를 다 썼을 때 null 처리하는 것입니다.

또한 null 처리한 참조를 실수로 사용하려 하면 프로그램은 즉시 NullPointerException 를 발생하여서 오류를 찾을 수 있습니다.

Null 처리를 언제 해야 할까?

객체 참조를 null 처리하는 일은 예외적인 경우여야 합니다. 바로 앞에서본 스택과 같은 경우에서 처럼 메모리를 직접 관리하는 경우에는 필요합니다.

개발자는 참조 영역이 비활성 영역이 되는 순간을 알고 있기 때문에, 해당 참조를 null 처리해서 해당 객체를 더는 쓰지 않을 것임을 가비지 컬렉터에 알려야 합니다. 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항시 메모리 누수에 주의해야 합니다.

이 외에도 메모리 누수를 일으킬 수 있는 부분들은 캐시 , 리스너(listener)와 콜백(callback) 이 있습니다.

객체를 Null로 설정하는 건 예외적인 상황에서나 하는것이지 평범한 일이 아닙니다.

정리

  • 자바의 가비지 컬렉터는 다 쓴 객체를 회수해 가지만 자칫 메모리 관리에 더 이상 신경쓰지 않아도 되는 것은 아닙니다.
  • 메모리 누수 는 가비지 컬렉션 활동량과 메모리 사용량이 늘어나게 되면 성능 저하로 인하여 발생할 수 있습니다. 심할 때는 디스크 페이징이나 OutOfMemoryError 를 일으켜 프로그램이 예기치 않게 종료될 수 있습니다.
    • 개발자는 참조 영역이 비활성 되는 순간을 미리 캐치해서 가바지 켈릭터에 알려야 합니다.

참고

반응형

댓글