조슈아 블로크 님의 "Effective Java" 책을 정리한 포스팅 입니다.
1. 자바의 메모리 관리
- GC를 통해 메모리를 자동으로 관리
- 모든 상황에서 GC가 적절히 회수하도록 보장할 수 없음
- 개발자가 메모리 누수를 방지하기 위한 추가 조치를 취해야 함
2. 메모리 누수 발생 사례
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);
}
}
문제점
- pop()
- 다 쓴 객체 참조를 제거하지 않음
- GC는 스택 객체 내부의 배열을 해석하지 못함
- 스택에서 사용되는 원소와 사용되지 않는 원소를 구분할 수 없음
해결책
public Object pop() {
if (size == 0) throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
- 다 쓴 참조를 명시적으로 null 처리하기
캐시
- 객체 참조를 캐시에 저장한 뒤, 사용되지 않는 참조를 제거해야 함
해결 방법
Map<Object, Object> cache = new WeakHashMap<>();
cache.put(key, value);
// key가 더 이상 참조되지 않으면, 자동으로 삭제
- WeakHashMap
- 약한 참조를 기반으로 객체 관리
- 한번 참조된 객체가 더 이상 사용되지 않으면 GC가 자동으로 제거
Map<Object, Object> cache = new LinkedHashMap<>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
return size() > 100; // 캐시 크기가 100을 넘으면 가장 오래된 항목 제거
}
};
- LinkedHashMap
- 유효 기간이 있는 캐시 자료구조
- 오래된 항목을 제거하는 캐시 정책 구현 가능
- removeEldestEntry 메서드 오버라이딩
'Java' 카테고리의 다른 글
[Effective Java] 3. 모든 객체의 공통 메서드 (0) | 2024.12.27 |
---|---|
[Effective Java] 2-8. 객체 생성과 파괴: 소멸자 사용을 피하라 (3) | 2024.12.27 |
[Effective Java] 2-6. 객체 생성과 파괴: 불필요한 객체 생성을 피하라 (0) | 2024.12.27 |
[Effective Java] 2-5. 객체 생성과 파괴: 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (2) | 2024.12.27 |
[Effective Java] 2-4. 객체 생성과 파괴: 인스턴스화를 막으려거든 private 생성자를 사용하라 (1) | 2024.12.27 |