스택을 구현한 아래의 코드를 살펴보자.
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());
}
}
위와 같은 코드에서는 GC가 동작하지 않아 메모리 누수가 발생할 수 있다. 스택에서 꺼내진 객체들을 더이상 프로그램에서 사용하지 않더라고 스택이 그 객체들의 다 쓴 참조(obsolete reference)를 가지고 있기 때문이다. 이러한 상황에서는 pop 부분에서 객체가 사용이 완료되었으면 GC가 해당 참조를 해제할 수 있도록 프로그램에서 null 처리를 해주면 된다. 이렇듯 자기의 메모리를 직접 관리하는 클래스라면 항시 메모리 누수에 주의해야 한다.
// 코드 7-2 제대로 구현한 pop 메서드 (37쪽)
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
메모리 누수를 일으키는 주범인 캐시를 처리하기 위해서는 WeakHashMap이나 Scheduled ThreadPoolExecutor를 활용하여 엔트리를 청소해주는 것이 좋다. 또한 리스너와 콜백의 문제에서 클라이언트가 콜백을 등록만 하고 해지하지 않을 경우에 가비지 컬렉터가 수거해갈 수 있도록 콜백을 weak reference로 설정해주는 것이 좋다. 메모리 누수는 힙 프로파일러와 같은 디버그 도구를 사용해서 발견될 수 있따.
'Reading > 이펙티브 자바' 카테고리의 다른 글
아이템6. 불필요한 객체 생성을 피하라. (0) | 2022.04.24 |
---|---|
아이템9 . try-finally 보다는 try-with-resource를 사용하라 (0) | 2022.04.24 |
아이템3. private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2022.04.24 |
아이템2. 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2022.04.24 |
아이템1. 생성자 대신 정적 팩터리 메서드를 고려하라 (0) | 2022.04.24 |