코드의 메모리 누수

아래 코드에서 메모리 누수가 일어나는 부분이 있습니다. pop() 에서 return만 하고 삭제하지 않고 있습니다.

public class StackExample {
    private  Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

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

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

    public Object pop(){
        if(size == 0){
            throw new EmptyStackException();
        }
        return elements[--size];
    }

    private void ensureCapacity() {
        if(elements.length == size){
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }

}

GC가 스택의 쓰레기 값을 회수할 수 있도록 null을 셋팅해줘야 합니다.

public Object pop(){
    if(size == 0){
        throw new EmptyStackException();
    }
    Object returnObject = elements[--size];
    elements[size] = null;
    return returnObject;
}

GC의 메모리 누수는 찾기 어렵다.

객체 참조 하나를 살려두면 가비지 컬렉터는 그 객체뿐만 아니라 그 객체를 참조하는 쓰레기 객체들도 회수하지 않습니다.

즉, 위처럼 스택 elements가 줄어들었어도 스택에서 꺼내진 객체들을 GC는 회수하지 않습니다. elements가 꺼내진 객체들을 참조(obsolete reference)하고 있기 때문입니다. 배열속에 쓰이지 않는 원소가 있어도 GC는 알 길이 없습니다.

이처럼 자기 메모리를 직접 관리하는 클래스는 메모리 누수에 조심해야 합니다.

GC에서 메모리를 관리하기 - null 처리를 통한 참조 해제

참조를 다 썼을 땐 null처리를 통해 참조 해제를 합니다. 하지만 null처리는 바람직하지 못합니다. 다 쓴 참조를 해제하는 가장 좋은 방법은 따로 있습니다.

GC에서 메모리를 관리하기 - 최소한의 scope

다 쓴 참조를 해제하는 방법은 그 참조를 담은 변수를 유효범위 밖으로 밀어내는 것입니다. 변수의 범위를 최소가 되게 정의했다면 자연스럽게 됩니다.

WeakHashMap

외부에서 Key를 참조하는 동안만 엔트리가 살아있는 캐시가 필요하면 WeakHashMap을 사용하자.

원리 GC는 Map이나 List의 값을 직접 비워주기 전까진 관여하지 않지만 WeakHashMap은 Key가 더이상 사용되지 않다면 제거합니다.

댓글남기기