일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 정렬
- select
- Greedy
- 백준
- 다이나믹프로그래밍
- 우선순위큐
- 알고리즘
- springboot
- 깊이우선탐색
- 프로그래머스
- SQL
- db
- 피보나치
- DP
- BFS
- join
- Spring
- Effective Java
- 데이터베이스
- 그리디알고리즘
- IntelliJ
- 너비우선탐색
- mariaDB
- Database
- java
- 탐욕법
- 이펙티브자바
- mybatis
- DFS
- 코테
- Today
- Total
땀두 블로그
[도서] Effective Java - Item 7. 다 쓴 객체 참조를 해제하라. 본문
이펙티브 자바 3판을 읽으면서 내용을 정리하는 포스트입니다. 혹시 틀린 부분이나 잘 못 설명한 부분이 있으면 댓글로 남겨주시면 수정하도록 하겠습니다.
Item 7. 다 쓴 객체 참조를 해제하라.
자바는 C/C++과는 다르게 가비지 컬렉터 덕분에 메모리 관리를 따로 안해주어도 된다는 장점이 있다. 하지만 이 또한 메모리를 절대적으로 관리해주는 것은 아니다. 책에 나온 예시 코드이다.
import java.util.Arrays;
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
this.elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public Stack(int size) {
this.elements = new Object[size];
}
public void push(final Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0) {
return null;
}
return elements[--size];
}
public Object size() {
return this.elements.length;
}
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, (size * 2) + 1);
}
}
}
이 코드를 보면 push, pop을 해주면서 따로 누수가 없는 것으로 보이지만, 메모리 누수가 존재한다. 실제로 elements[--size]라는 부분에서 실제 값이 삭제되는 것이 아니라 배열 내부의 인덱스만 이동하기 때문이다. 이러한 메모리 누수를 관리해주지 않으면 심한 경우 디스크 페이징
이나 OutOfMemoryError
를 일으켜 프로그램이 예기치 않게 종료되기도 한다.
이러한 문제는 pop() 함수에서 값을 반환하기 전에 null로 초기화 해주면 된다.
...
public Object pop() {
if (size == 0) {
return new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null;
return result;
}
...
이 방식을 사용하면 문제가 생긴다고 해도 NullPointerException
이 발생해 더 큰 문제가 발생하는 것을 사전에 차단할 수 있다. 하지만 개발자가 모든 코드에 대해서 null처리를 해주는 것은 가독성을 낮추기 때문에 예외적인 경우에만 null처리를 하도록 해야 한다.
다 쓴 참조를 해제하는 가장 좋은 방법은 참조를 담은 변수를 유효 범위 밖으로 밀어내는 것이다. 이는 아이템 57에서 나올 예정이다.
Stack이 메모리 누수에 취약한 이유
- 자기 자신의 메모리를 직접 관리하기 때문
- 객체가 아닌 객체 참조를 담는
elements 배열
로 저장소 풀을 만들어 원소들을 관리함 - 배열의 활성 영역부분에 속한 원소들은 사용되고, 비활성 영역은 쓰이지 않음
- 비활성 영역을 가비지 컬렉터가 알 방법이 없다는 것
stack 이외에도 자신의 메모리를 직접 관리하는 클래스는 프로그래머가 항상 메모리 누수에 주의해야 한다.
또 다른 메모리 누수의 주범인 캐시와 리스너(Listener) 혹은 콜백(Callback)이 있다.
캐시의 메모리 누수 해결방법
- 캐시 외부에서 키를 참조하는 동안만 엔트리가 살아 있는 캐시가 필요한 경우에는 WeakHashMap을 사용
- 엔트리의 유효 기간을 정해서 사용
- 이 방법은 유효 기간을 계산하는 것이 어렵기 때문에 엔트리 가치를 떨어뜨리는 방식을 사용한다.
- 쓰지 않는 엔트리를 청소해준다.
ScheduledThreadPoolExecutor
와 같은 백그라운드 스레드를 활용하거나 캐시에 새 엔트리를 추가할 때 부수 작업으로 수행하는 방법을 이용하면 된다.LinkedHashMap
은removeEldestEntry
메소드를 사용해 후자의 방식으로 처리한다.
리스너(Listener) 혹은 콜백(Callback)의 메모리 누수 해결방법
클라이언트가 콜백을 등록만 하고 해지하지 않는다면 콜백은 쌓이게 될 것이다. 이럴 때 콜백을 약한 참조(weak reference)로 저장하면 GC가 즉시 수거해간다. 예시로 WeakHashMap
에 키로 저장하는 방법이 있다.
'도서' 카테고리의 다른 글
[도서] Effective Java - Item 9. try-finally보다는 try-with-resource를 사용하라. (0) | 2022.04.16 |
---|---|
[도서] Effective Java - Item 8. finalizer와 cleaner 사용을 피하라 (0) | 2022.04.16 |
[도서] Effective Java - Item 6. 불필요한 객체 생성을 피하라. (0) | 2022.04.16 |
[도서] Effective Java - Item 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라. (0) | 2022.04.15 |
[도서] Effective Java - Item 4. 인스턴스화를 막으려거든 private 생성자를 사용하라. (0) | 2022.04.15 |