[Effective Java] ch1 - item7. 다 쓴 객체 참조를 해제하라
코드의 메모리 누수
아래 코드에서 메모리 누수가 일어나는 부분이 있습니다.
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가 더이상 사용되지 않다면 제거합니다.
댓글남기기