자바같은 가비지 컬렉션을 가진 언어에서는 메모리 누수를 찾기가 매우 어렵다.객체 참조를 하나라도 살려두면 가비지 컬렉터는 그 객체뿐 아니라 그 객체가 참조하는 모든 객체를 회수하지 못 한다.
이를 해결하는 방법은 간단하다. 해당 참조를 다 썻을 때 null 처리 해주면 된다.
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]; /* memory leak point */
}
private void ensureCapacity() {
if (this.elements.length == size) {
this.elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object value = this.elements[--size];
this.elements[size] = null;
return value;
}
다 사용한 참조를 null처리하면 부가적인 이점도 있다.
해당 참조를 실수로 재사용하게 된다면 프로그램은 즉시 nullPointerException을 던지며 종료하게 된다.
그러나 객체 참조를 null 처리하는 일은 예외적인 경우에만 사용해야 한다.
다 쓴 참조를 해제하는 가장 좋은 방법은 그 참조를 담은 변수를 유효 범위(scope) 밖으로 밀어내는 것이다.
그럼 null 처리는 언제 해야 할까?
일반적으로 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항시 메모리 누수에 주의해야 한다.(List / Stack / Queue 등의 원소에서의 객체 참조)
캐시 역시 메모리 누수를 일으킨다.
객체 참조를 캐시에 넣고, 이 사실을 까맣게 잊은 채 그 객체를 다 쓴 뒤로도 한참을 그냥 놔두는 일을 자주 접할 수 있다.
엔트리가 살아 있는 캐시가 필요한 상황이라면 WeakHashMap을 사용해 캐시를 만들자.
다 쓴 엔트리는 그 즉시 자동으로 제거될 것이다.
그러나 캐시를 만들 때 유효기간을 정확히 정의하기 어렵다.
그래서 시간이 지날수록 엔트리의 가치를 떨어뜨리는 방식을 흔히 사용한다.
LinkedHashMap은 EldestEntry 메서드를 통해 이 방법을 사용할 수 있게된다
리스너와 콜백도 메모리 누수를 발생시킬 수 있는데, 클라이언트가 콜백을 등록만 하고 해지하지 않는다면, 뭔가 조치해주지 않는 한 콜백은 계속해서 쌓여갈 것이다.
이럴 때 약한 참조로 저장하면 가비지 컬렉터가 즉시 수거해간다.
- WeakHashMap
- WeakHashMap은 캐시 바깥에서 키(key)를 참조하고 있을 때만 값(value)를 보관한다
키에 대한 참조가 만기참조가 되는 순간 자동으로 캐시 안에 <키,값> 쌍은 삭제된다
따라서 주의할 점은, 캐시 안에 보관된 항목의 수명이 키에 대한 외부참조의 수명에 따라 결정되는 상황에만 적용 가능하다.
- WeakHashMap은 캐시 바깥에서 키(key)를 참조하고 있을 때만 값(value)를 보관한다
- LinkedHashMap
- 캐시에 새로운 항목이 추가될 때, removeEldestEntry 메소드가 실행된다.
따라서 캐시에 항목이 추가될 때마다, 가장 오래된 캐시를 제거할 수 있다.
- 캐시에 새로운 항목이 추가될 때, removeEldestEntry 메소드가 실행된다.
'독후감 > Effective Java' 카테고리의 다른 글
9. try-finally 보다는 try-with-resources를 사용하라 (0) | 2021.04.03 |
---|---|
8. finalizer와 cleaner 사용을 피하라 (0) | 2021.04.02 |
6. 불필요한 객체 생성을 피하라 (0) | 2021.03.31 |
5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2021.03.30 |
4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2021.03.18 |