GC가 있어도 메모리 누수가 발생할 수 있음
// 코드 7-1 메모리 누수가 일어나는 위치는 어디인가? (36쪽)
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를 참조하고 있기 때문에 GC가 회수하지 않음
// 코드 7-2 제대로 구현한 pop 메서드 (37쪽)
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
null 처리를 해주면 참조 해제가 됨
-> null 처리를 하게 되면 코드가 필요 이상으로 지저분해짐, 자기 메모리를 직접 관리하는 클래스에서 사용할 것
일반적인 경우엔 유효 범위 밖으로 밀어내는 방법을 사용할 것
유효 범위 밖으로 밀어내는 것이란?
-> GC의 대상이 되게 만드는 것으로 이해했음
public int globalVar;
public void method1() {
globalVar = 10; // 메서드가 끝나도 GC의 대상이 되지 않음
}
public void method2() {
int localVar = 10; // 메서드가 끝나면 GC의 대상이 됨
}
캐시도 메모리 누수를 일으키는 주범 (객체 참조를 캐시에 넣어놓고 까먹을 수 있기 때문에)
-> WeakHashMap을 사용하면, 키에 해당하는 객체가 더 이상 사용되지 않으면 해당 엔트리는 자동으로 제거된다
WeakHashMap - http://blog.breakingthat.com/2018/08/26/java-collection-map-weakhashmap/
-> LinkedHashMap을 사용하면 가장 오래된 entry를 제거하는 removeEldestEntry 메서드를 사용할 수 있다
LinkedHashMap - https://javafactory.tistory.com/735
리스너와 콜백 역시 메모리 누수의 주범 (콜백을 등록만 해놓고 해지하지 않는 경우, 콜백이 계속 쌓임)