
Stack으로 본 객체 참조 해제
아래의 Stack
클래스 코드를 확인해보고 문제가 될 만한 부분을 찾아보자.
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);
}
}
}
특별히 문제가 보이진 않지만, 위의 코드는 꼭꼭 숨어 있는 메모리 누수 문제가 있다.
어디서 메모리 누수가 일어날까?
바로 스택에서 꺼내진 객체들을 가비지 컬렉터가 회수하지 않기 때문이다.
프로그램에서 해당 객체들을 더 이상 사용하지 않더라도, 스택이 그 객체에 대한 참조를 가지고 있어서 가비지 컬렉터는 회수해가지 못한다.
객체 참조를 어디든 하나라도 살려두면 가비지 컬렉터는 그 객체뿐만 아니라 그 객체가 참조하는 모든 객체를 회수해가지 못한다.
따라서 이를 해결하기 위해서는 해당 참조를 다 썼을 때 null
처리를 해줘야 한다.
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null;
return result;
}
객체 참조 해제 시 주의해야할 점
위와 같은 문제를 보고 걱정이 되어 모든 객체를 다 쓰자마자 일일이 null
처리할 필요는 없다. 또한 바람직하지도 않다.
객체 참조를 null
처리하는 일은 예외적인 경우여야 한다.
그렇다면 언제 null 처리를 해야 할까?
일반적으로 Stack
클래스와 같이 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항시 메모리 누수에 주의해야 한다.
캐시와 메모리 누수
캐시 역시 메모리 누수를 일으키는 주범이다.
객체 참조를 캐시에 넣고 나서, 다 쓴 뒤에 한참을 그냥 놔두는 일을 자주 접할 수 있다.
캐시 외부에서 키를 참조하는 동안만 엔트리가 살아 있는 캐시가 필요한 상황이라면 WeakHashMap
을 사용해 캐시를 만들자.
캐시를 만들 때 유효 기간을 정확히 정의하기는 어렵다. 이럴 때는
ScheduledThreadPoolExecutor
와 같은 백그라운드 스레드를 활용하거나- 캐시에 새 엔트리를 추가할 때 부수 작업을 하는 방법이 있다(
LinkedHashMap
의removeEldestEntry
).
리스너(listenr)와 콜백(callback)과 메모리 누수
클라이언트가 콜백을 등록만 하고 명확히 해지하지 않는다면 콜백은 계속 쌓이게 된다.
이럴 때는 약한 참조(weak reference)를 사용한다. ex. WeakHashMap
메모리 누수 팁
메모리 누수는 철저한 코드 리뷰나 힙 프로파일러 같은 디버깅 도구를 동원해야만 발견되기도 한다.
그래서 이런 종류의 문제는 예방법을 익혀두는 것이 매우 중요하다.
'Java > Effective Java' 카테고리의 다른 글
Effective Java 9. try-with-resources를 사용하자 (0) | 2022.10.04 |
---|---|
Effective Java 6. 불필요한 객체 생성을 피하라 (0) | 2022.01.06 |
Effective Java 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2022.01.04 |
Effective Java 4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2022.01.02 |
Effective Java 3. 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2021.12.28 |