Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- 배낭문제
- 파이썬
- cache
- 멱등성
- BFS
- spring
- JVM
- collapse
- 코딩테스트
- 타임리프
- thymeleaf
- 이펙티브자바
- @Transactional
- interceptor
- 클린아키텍처
- Spring Security
- AOP
- Java
- JPA
- Garbage Collection
- 동시성처리
- 자바
- lombok
- TDD
- 알고리즘
- effective java
- 캐시
- EntityGraph
- Transactional
- EffectiveJava
Archives
- Today
- Total
Jinnie devlog
[Effective Java] 다 쓴 객체 참조를 해제하라 본문
C, C++와 같은 언어는 메모리를 직접 관리해야 하지만, 자바는 가비지 컬렉터를 갖추어서 다 쓴 객체를 알아서 회수한다. 자칫 메모리 관리에 더 이상 신경 쓰지 않아도 된다고 오해할 수 있는데, 사실이 아니다.
예제
public class Stack{
private Object[] elements;
private int size = 0;
private Stack(){
elements = new Object[DEFAULT_INITIAL_COPACITY];
}
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)를 여전히 가지고 있기 때문이다.
가비지 컬렉션 언어에서는 메모리 누수를 찾기가 아주 까다롭다.
객체 참조 하나를 살려두면 가비지 컬렉터는 그 객체뿐 아니라 그 객체가 참조하는 모든 객체를 회수해가지 못한다.
해법
해법은 해당 참조를 다 썼을 때 null 처리를 하여 참조를 해제하면 된다.
public Object pop(){
if(size==0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; //다 쓴 참조 해제
reutrn result;
}
각 원소의 참조가 더 이상 필요 없어지는 시점(스택에서 꺼내질 때)에 null 처리를 해준다.
메모리 누수의 원인
1. 자기 메모리를 직접 관리하는 클래스
자기 메모리를 직접 관리하는 클래스(Stack)라면 프로그래머는 항상 메모리 누수에 주의해야 한다. 원소를 다 사용한 즉시 그 원소가 참조한 객체들을 다 null 처리해줘야 한다.
2. 캐시
객체 참조를 캐시에 넣고나서, 그 객체를 다 쓴 뒤로도 한참을 그냥 놔두면 메모리 누수가 생긴다.
해법
- 캐시 외부에서 키를 참조하는 동안만 엔트리가 살아있는 캐시가 필요한 상황이라면, WeakHashMap을 사용해 캐시를 만든다. 그러면 다 쓴 엔트리는 즉시 자동으로 제거된다.
- 캐시 엔트리의 유효기간을 정확히 알기 어려워 시간이 지날수록 엔트리의 가치를 떨어뜨리는 방식에서는 쓰지 않는 엔트리를 청소해줘야한다.
- ScheduledThreadPoolExcutor 같은 백그라운드 스레드를 활용
- 캐시에 새 엔트리를 추가할때 부수 작업으로 청소를 수행 ex) LinkedHashMap은 removeEldestEntry 메서드를 활용
- 더 복잡한 캐시를 만들고 싶다면 java.lang.ref 패키지를 직접 활용해야 한다.
3. 리스너 / 콜백
클라이언트가 콜백을 등록만 하고 명확하게 해지하지 않으면, 콜백이 계속 쌓여갈 것이다. 이럴 때 콜백을 약한 참조(weak reference)로 저장하면 가비지 컬렉터가 즉시 수거해간다. 예를 들어 WeakHashMap에 키로 저장하면 된다.
'Java' 카테고리의 다른 글
[Effective Java] clone 재정의는 주의해서 진행하라 (1) | 2023.02.02 |
---|---|
[Effective Java] toString을 항상 재정의하라 (0) | 2023.02.02 |
[Effective Java] try-finally보다는 try-with-resources를 사용하라 (0) | 2023.02.02 |
[Effective Java] 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2023.02.02 |
[Effective Java] private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2023.02.02 |