[Why]
- 자바처럼 가비지 컬렉터를 갖춘 언어는, 다 쓴 객체를 알아서 회수해가기 때문에 자칫 메모리 관리에 더 이상 신경을 쓰지 않아도 된다고 오해할 수 있는데, 절대 사실이 아니다.
[When]
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);
}
}
}
스택
- 스택이 커졌다가 줄어들었을 때 스택에서 꺼내진 객체들을 가비지 컬렉터가 회수하지 않는다.
- 이 스택이 그 객체들의 다 쓴 참조(obsolete reference)를 여전히 가지고 있기 때문이다.
- 위 코드에서는 elements 배열의 '활성 영역' 밖의 찹조들이 모두 여기 해당한다. 활성 영역은 인덱스가 size보다 작은 원소들로 구성된다.
캐시
- 캐시 역시 메모리 누수를 일으키는 주범이다. 객체 참조를 캐시에 넣고 나서, 이 사실을 까맣게 잊은 채 그 객체를 다 쓴 뒤로도 한참을 그냥 놔두는 일을 자주 접할 수 있다.
리스너(listner) / 콜백(callback)
- 클라이언트가 콜백을 등록만 하고 명확히 해지하지 않는다면, 뭔가 조치해주지 않는 한 콜백은 계속 쌓여갈 것이다.
[How]
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 하제
return result;
}
null 처리하기
- 해당 참조를 다 썼을 때 null 처리(참조 해제)하면 된다. 객체 참조를 null 처리하는 일은 예외적인 경우여야 한다.
- null 처리는 언제 해야 할까? 일반적으로 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항시 메모리 누수에 주의해야 한다.
변수의 scope
- 다 쓴 참조를 해제하는 가장 좋은 방법은 그 참조를 담은 변수를 유효 범위(scope) 밖으로 밀어내는 것이다.
- 변수의 범위를 최소가 되게 정의했다면아이템57 이 일은 자연스럽게 이뤄진다.
WeakHashMap
- 캐시 외부에서 키(key)를 참조하는 동안만 엔트리가 살아있는 캐시가 필요한 상황이라면 WeakHashMap을 사용해 캐시를 만든다. 다 쓴 엔트리는 그 즉시 자동으로 제거될 것이다.
- 콜백도 약한 참조(weak reference)로 저장하면 가비지 컬렉터가 즉시 수거해간다. 예를 들어 WeakHashMap에 키로 저장하면 된다.
'독서찰기(讀書札記) > 이펙티브 자바' 카테고리의 다른 글
[아이템 9] try-finally보다는 try-with-resources를 사용하라 (0) | 2022.01.08 |
---|---|
[아이템 8] finalizer와 cleaner 사용을 피하라 (0) | 2022.01.08 |
[아이템 6] 불필요한 객체 생성을 피하라 (0) | 2022.01.07 |
[아이템 5] 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2022.01.06 |
[아이템 4] 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2022.01.05 |