자바에서는 가비지 컬렉터가 다 쓴 객체를 알아서 회수해가니 메모리관리에서 더이상 신경 쓰지 않아도 된다고 오해할 수 있는데 절대 사실이 아니다.
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
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);
}
public static void main(String[] args) {
Stack stack = new Stack();
for (String arg : args)
stack.push(arg);
while (true)
System.err.println(stack.pop());
}
}
해당 코드는 왠만한 테스트에도 거뜬히 통과할 것이다. 하지만 스택에 계속 넣다가 빼낼때 스택이 차지하는 메모리는 줄어들지 않는다. 왜냐면 저 스택의 구현체는 필요없는 객체에 대한 레퍼런스를 그대로 가지고 있기 때문이다. 가용 범위(실제 값들을 들고 있는 부분)는 size보다 작은 부분이고 그 값보다 큰 부분에 있는 값들은 필요없이 메모리를 차지하고 있는 부분
// 코드 7-2 제대로 구현한 pop 메서드 (37쪽)
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
스택에서 꺼낼 때 그위치에 있는 객체를 꺼내주고 그 자리를 null
로 설정해서 다음 GC가 발생할 때 레퍼런스가 정리되게 한다. 실수로 해당 위치에 있는 객체를 다시 꺼내는 경우에는 NullPointerException
이 발생할 수 있다.
그렇다고 필요없는 객체를 볼 때 마다 null
로 설정하는 코드를 작성하지 말라. 객체를 null로 설정하는 건 예외적인 상황에서나 하는 것이지 평범한 일이 아니다. 다 쓴 참조를 해제하는 가장 좋은 방법은 그 참조를 담은 변수를 유효범위밖으로 밀어내는 것이다.
null로 처리하는 상황은? 해당 스택은 자기 메모리를 직접 관리하기 때문에 elements 배열로 저장소 풀을 만들어 자기 원소들을 관리한다.즉 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항시 메모리 누수에 주의해야 한다.
캐시를 사용할 때도 메모리 누수 무제를 조심해야 한다. 객체 참조를 캐시에 넣고 나서 이 사실을 까맣게 잊은 채 그 객체를 다 쓴 뒤에도 한참을 그냥 놔두는 일을 자주 접한다.
여러 해결책이 있지만, 캐시의 키에 대한 레퍼런스가 캐시 밖에서 필요 없어지면 해당 엔트리를 캐시에서 자동으로 비워주는 WeakHashMap
을 쓸 수 있다.
세 번재 주범은 리스너 혹은 콜백이다.
클라이언트가 콜백을 등록만 하고 명확히 해지하지 않는다면, 뭔가 조치해주지 않는 한 콜백은 계속해서 쌓여갈 것이다. 이럴 때 콜백을 얗나 참조로 저장하면 가비지 컬렉터가 즉시 수거해간다. 키를 WeakHashMap
으로 저장하면된다.
메모리 누수는 발견하기 쉽지 않기 때문에 수년간 시스템에 머물러 있을 수도 있다. 코드 인스택션이나 heap profiler 같은 디버깅 툴을 사용해서 찾아야 한다. 따라서 이런 문제를 예방하는 방법을 학습하여 미연에 방지하는 것이 좋다.