728x90
가비지 컬렉터가 다 쓴 객체를 알아서 회수해간다고 메모리 관리에 신경 쓰지 않아도 된다는 것은 아니다.
메모리 누수의 예
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);
}
}
이 스택을 사용하는 프로그램은 메모리 누수가 발생해 결국 성능이 저하된다.
메모리 누수의 원인
스택에서 pop 된 객체들을 가비지 컬렉터가 회수하지 않는다.
스택의 elements 배열의 활성 영역 밖의 다 쓴 참조를 가지고 있기 때문이다.
- 활성 영역 : 인덱스가 size보다 작은 원소들
- 다 쓴 참조 : 다시 쓰지 않을 참조
메모리 누수 해결 방법
참조를 다 썼을 때(스택에서 꺼내질 때) Null 처리(참조 회수)를 하면 된다.
null 처리된 참조를 사용하려고 하면, NullPointerException이 발생하는 이점도 있다.
public Obejct pop() {
if(size == 0)
throw new EmptyStackException();
Obejct result = elements[--size];
elements[size] = null; //다 쓴 참조 해제
}
객체 참조를 null 처리하는 일은 예외적인 경우여야 한다.
메모리 누수를 일으키는 주범
자기 메모리를 직접 관리하는 클래스
앞선 예제 스택은 객체의 참조를 담고 있는 element 배열로 저장소 풀을 만들어 원소들을 관리하는 클래스이다.
가비지 컬렉터 입장에서는 비활성 영역에서 참조하는 객체도 똑같이 유효한 객체이다.(비활성 영역의 객체가 쓸모없다는 건 개발자만이 알고 있다)
해결 방법
원소를 다 사용한 즉시 그 원소가 참조한 객체들을 다 null처리를 해줘야 한다.
캐시
객체 참조를 캐시에 넣고, 다 쓴 뒤로도 그냥 놔두는 경우
해결 방법
캐시 외부에서 키(key)를 참조하는 동안만 엔트리가 필요한 상황이라면 WeakHashMap을 사용해 캐시를 만들자
- 키 값이 더 이상 사용되지 않는다고 판단되면 다음 GC때 해당 key, value 쌍을 제거한다.
시간이 지날수록 엔트리의 가치를 떨어뜨리는 방식
- SceheduledThreadPoolExecutor와 같은 백그라운드 스레드 활용
- 캐시에 새 엔트리를 추가할 때 부수 작업으로 수행 : LinkedHashMap의 removeEldestEntry 메서드
리스너(Listener) 혹은 콜백(callback)
클라이언트가 콜백을 등록하고 명확하게 해지하지 않으면, 콜백은 계속 쌓인다.
해결 방법
콜백을 약한 참조로 저장하면 가비지 컬렉터가 즉시 수거해간다. -> 예를 들어 WeakHashMap에 키로 저장
참고
- Java Reference와 GC -> 모든 내용을 다 이해할 순 없었지만 도움이 되었다.
- (Java) 참조 유형 (Strong Reference/ Soft Reference/ Weak Reference/ Phantom References)
'책읽고 정리 > Effective Java' 카테고리의 다른 글
[Effective Java 3/E] 아이템9. try-finally보다는 try-with-resources를 사용하라 (0) | 2021.06.22 |
---|---|
[Effective Java 3/E] 아이템6. 불필요한 객체 생성을 피하라 (0) | 2021.06.10 |
[Effective Java 3/E] 아이템5. 자원을 직접 명시하지 않고 의존 객체 주입을 사용하라 (0) | 2021.05.29 |
[Effective Java 3/E] 아이템4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2021.05.25 |
[Effective Java 3/E] 아이템3. private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2021.05.24 |
댓글