메모리 누수의 원인들
•
자바에서는 가비지 컬렉터를 통해 더 이상 사용하지 않는 자원들에 대해 자동적으로 수거된다.
•
하지만 객체들을 다 사용하고 나서도 참조를 해제 하지 않는다면, 가비지 콜렉터는 해당 객체를 사용하고 있다고 판단해 수거를 하지 않고 메모리 누수로 이어진다.
•
아래는 스택을 구현한 코드이다.
public class Stack {
private Object[] elements;
private int size = 0;
...
public Object pop() {
if (size == 0)
throw new EmptyStackException();
return element[--size];
}
}
Java
복사
•
위와 같이 pop 메서드를 구현하면, 실제 element는 줄어든 size까지만 사용되는데 그 뒤의 배열에는 여전히 참조가 남아있기 때문에 메모리 누수가 발생한다.
•
가비지 컬렉션 언어에서는 메모리 누수를 찾기가 상당히 까다로운데, 그렇기 때문에 아래처럼 다 쓴 객체는 null 처리(참조 해제)하자.
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
Java
복사
•
이처럼 다쓴 참조 객체를 null 처리하면, 실수로라도 해당 참조를 사용할 때 NullPointerException이 발생해서 오동작 하지 않고 잘못된 사용임을 개발자에게 알릴 수 있다.
•
객체 참조를 다 사용했다고 하여 무조건적으로 null 처리하는 것은 바람직하지 않고, 예외적인 경우에만 사용하자.
•
위의 Stack 클래스의 경우에는 내부에 멤버 변수로 원소들의 배열을 들고 자기가 직접 관리한다. 이와 같은 경우에는 가비지 컬렉터가 비활성 영역인지 아닌지 알 수 없기 때문에 메모리를 수거해가지 않는다.
•
그러니 이처럼 자기가 메모리를 직접 관리하는 클래스인 경우에는 메모리 누수에 주의하자.
•
캐시 또한 메모리 누수를 자주 발생시키는데, 데이터를 캐싱 후 한참동안 해제하지 않고 놔두는 일이 흔하기 때문이다.
•
WeakHashMap은 엔트리가 살아있는 동안 캐시를 사용하고, 다 쓴 엔트리는 즉시 제거한다.
•
이처럼 시간이 지날 수록 엔트리의 가치를 떨어뜨리는 방식으로, 엔트리를 가끔씩 청소해주어야 한다.
•
리스터(listener) 혹은 콜백(callback) 또한 메모리 누수를 발생시킬 수 있다. 콜백을 등록 후 명확히 해지하지 않으면, 콜백을 계속 쌓여갈 것이다.
•
콜백을 약한 참조(weak reference)로 저장하면 가비지 컬렉터가 즉시 수거해가니, WeakHashMap에 키로 저장하는 방식을 사용하자.