이 포스팅에서 작성하는 내용은 EFFECTIVE JAVA(이펙티브자바) 에서 발췌하였습니다.
아이템 7. 다 쓴 객체 참조를 해제하라
C/C++ 과는 다르게 Java는 GC(Garbage Collector)를 갖추고 있기 때문에 다 쓴 객체를 알아서 회수한다.
하지만, GC가 있다고 해서 메모리 관리에 신경쓰지 않아도 되는 것은 아니다.
1. Stack
스택을 간단히 구현하면 아래와 같다.
1) 메모리 누수가 발생하는 코드
- 기능적으로는 전혀 문제가 없음
- 메모리 누수가 발생함
- 스택이 커졌다가 줄어들었을 때, 스택에서 꺼내진 객체들을 GC가 회수하지 않음
- 스택에서 꺼내진 객체들은 스택에서 여전히 해당 객체들을 바라보고 있기 때문 (이를 '다 쓴 참조(obsolete reference)' 라고 함)
- 다 쓴 참조(obsolete reference) : 문자 그대로 앞으로 다시 쓰지 않을 참조
- 객체 참조를 하나 살려두면 그 객체가 참조하는 모든 객체(또 그 객체들이 참조하는 연관된 객체들 모두)를 회수할 수 없다. → 몇 개의 객체가 매우 많은 객체를 참조한다면 회수를 못하여 성능이 안좋아질 수 있다.
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CPAPCITY = 16;
public statk() {
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];
}
// 원소를 위한 공간을 확보하는 메서드
// 배열의 크기를 늘려야 할 때마다 약 2배 씩 늘림
private void ensureCapacity() {
if(elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
2) 위의 코드에서 pop() 메서드를 보완한 코드
- pop()으로 객체를 반환해주면서 null로 참조를 해제
public class Stack {
...
public Object pop() {
if(size == 0) {
throw new EmptyStackException();
Object data = elements[--size];
elements[size] = null; // null 을 넣어줌으로써 객체 참조 해제
return data;
}
...
}
모든 객체를 다 쓰자마자 일일이 null 처리를 해야할 까?
결론은 '객체 참조를 null 처리하는 일은 예외적인 경우여야 한다.'
다 쓴 참조를 해제하는 가장 좋은 방법은 그 참조를 담은 변수를 유효 범위(scper) 밖으로 밀어내면 된다.
* null 처리를 해야하는 경우
스택 클래스의 경우 GC가 아닌 자기 메모리를 직접 관리하기 때문에, 프로그래머가 null 처리를 하여 해당 객체를 더 이상 쓰지 않겠다고 GC에 알려야 한다.
즉, 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항시 메모리 누수에 주의해야 한다.
* 캐시 사용시에도 조심해야 한다.
캐시 외부에서 키를 참조하는 동안만 엔트리가 살아있는 캐시가 필요한 상황이라면 WeakHashMap을 사용하면 좋다.
(위의 상황에서만 유용)
* 리스너, 콜백도 조심해야 한다.
클라이언트가 콜백을 등록만 하고 명확히 해지하지 않는다면 콜백은 계속 쌓여간다.
→ 콜백을 약한 참조로 저장하면 GC가 즉시 수거 (ex : WeakHashMap에 키로 저장)
정리
1. 자기 메모리르 직접 관리하는 클래스에서는 다 쓴 객체의 참조를 해제하자.
2. 캐시, 리스너, 콜백을 사용할 때 주의하자.
'BE > Java' 카테고리의 다른 글
[Effective Java] 아이템 9. try-finally보다는 try-with-resources를 사용하라 (0) | 2022.09.07 |
---|---|
[Effective Java] 아이템 8. finalizer와 cleaner 사용을 피하라 (0) | 2022.09.06 |
[Effective Java] 아이템 6. 불필요한 객체 생성을 피하라 (0) | 2022.09.02 |
[Effective Java] 아이템 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2022.09.01 |
[Effective Java] 아이템 4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2022.08.31 |