🎯 학습 목표
- GC(Garbage Collector)의 동작
- 메모리를 관리하는 객체
📌 객체 참조를 해제하라!
C 계열 개발자가 부러워하는 GC는 개발자가 직접적으로 메모리를 관리하지 않아도 되게 해준다.
하지만 GC가 있다고 절대적으로 메모리 누수가 발생하지 않는 것은 아니다.
책에는 다음과 같은 예시를 제공한다.
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);
}
}
그냥 봤을 때는 이상이 전혀 없지만 메모리 누수가 발생하고 있다.
스택에서 꺼내진 객체들을 더 이상 사용되지 않더라도 (다 쓴 참조) GC가 회수하지 않는다.
GC는 살아있는 참조가 하나라도 있으면 그 참조와 연관된 모든 메모리를 회수하지 않는다.
🤔 왜 회수하지 못할까?
추적(Tracing) 기반으로 작동하는 오늘날 JAVA의 GC는 항상 접근 가능한 메모리(root)를 기반으로 동작한다.
root를 기반으로 도달할 수 있는 모든 객체의 참조를 찾아낸다. 여기서 도달할 수 없는 객체의 참조가 회수 대상이다.
GC가 객체를 회수하기 위해선 해당 객체가 참조하는 모든 객체도 회수해야 하고 이 과정은 재귀적으로 이루어진다.
따라서, 위의 스택이 살아있는 한 메모리 누수가 계속 발생할 것이고 나아가 프로그램 자체를 종료시킬 수도 있다.
😉 해결 방법
간단하게 다 쓴 참조를 null
처리해서 해결할 수도 있다.
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return elements[--size];
}
하지만 무조건적으로 null
을 넣어주는 것이 좋은 것만은 아니기 때문에 이와 같이 특별한 상황에만 써야 한다.
즉, 자기 메모리를 직접 관리하는 객체에서 다 쓴 메모리의 참조를 해제해야 한다.
😎 그 외의 메모리 누수
객체 참조를 캐시에 넣어놓는 경우에도 캐시의 참조가 해제되지 않는다면 메모리가 줄줄 샌다.
또한, 리스너나 콜백에서도 누수가 발생할 수 있다.
😋 정리
결국 GC의 동작원리를 이해하고, 스스로 메모리를 관리하는 객체에서 메모리 누수가 발생할 수 있다는 것만 기억하자!
-Reference
https://medium.com/@joongwon/jvm-garbage-collection-algorithms-3869b7b0aa6f
😋 지극히 개인적인 블로그지만 훈수와 조언은 제 성장에 도움이 됩니다 😋
'개인 공부 > Java (이펙티브 자바)' 카테고리의 다른 글
[이펙티브 자바] 아이템 13 : clone 재정의는 주의해서 진행하라 (0) | 2023.02.01 |
---|---|
[이펙티브 자바] 아이템 10 : equals는 일반 규약을 지켜 재정의하라 (0) | 2023.01.31 |
[이펙티브 자바] 아이템 6 : 불필요한 객체 생성을 피하라 (0) | 2023.01.15 |
[이펙티브 자바] 아이템 5 : 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2023.01.15 |
[이펙티브 자바] 아이템 4 : 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2023.01.02 |
댓글