<Effective Java> RULE 6 유효기간이 지난 객체 참조는 폐기하라
가비지 콜렉터가존재하는 언어를 사용하면 볼일없는 객체는 자동으로 반환되는데 신경쓰지 않다보면 메모리관리가 필요하다는것을 망각할 수 있다.
예) 스택 : 메모리누수가 어디서 생기는가?
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];
}
/**
* 적어도 하나이상의 원소를 담을 공간을 보장한다.
* 배열의 길이를 늘려야할 때마다 약 2배씩 증가된다.
*/
private void ensureCapacity(){
if(elements.length == size){
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
메무리 누수?
→ 스택이 커졌다 줄어들면서 제거한 객체들을 가비지 콜렉터가 처리하지 못하여 생기는 문제
→ 스택이 만기참조(다시 이용되지 않을 참조)를 제거하지 않기 때문에 발생
→ elements배열에서 실제로 사용되는 부분 외에 부분에 보관된 참조들이 만기 참조이다.
→ size외의 영역(의도되지 않은 객체 보유)
객체 참조를 유지할 경우 해당 객체만 GC에서 제외는 것이 아니라 그 객체를 통해 참조되는 다른 객체들도 GC에서 제외된다.
따라서 쓸일 없는 객체 참조는 무조건 null로 만들어야 한다.
Public Object pop(){
if(size = 0){
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;//만기참조 제거
return results;
}
만기참조를 제거하는 가장 좋은 방법
→ 해당 참조가 보관된 변수가 유효범위(scope)를 벗어나게 두고 변수를 정의할 때 유효범위를 최대한 좁게 만들면 자연스럽게 해결된다.(규칙 45)
→ 처음으로 사용하는 곳에서 선언
→ 거의 모든 지역변수 선언에는 초기값 포함
→ while문 보다는 for문을 써라
→ 순환문 변수를 선언하여 그 유효범위가 선언된 지역으로 제한
null처리는 언제하는것이 좋을까?
→ 스택에서 elements는 GC가 어느것이 유효한것인지 확인할 방법이 없다.
→ 하지만 사용하지 않는 참조들을 즉시 null로 만들면 GC는 어떤것이 반환해야하는지 알 수 있다.
자체적으로 관리하는 메모리가 있는 클래스를 만들때는 메모리 누수가 발생하지 않도록 주의해야 한다.
흔히 메모리누수가 발생하는것
→ 캐시cache : 객체 참조를 캐시 안에 넣어 놓고 잊어버리는 일이 많기 때문이다. (수명이 키에 대한 외부 참조의 수명에 따라 결정되는 상황에는 WeakHashMap 활용)
→ 리스너listener등의 역호출자(callback)콜백을 명시적으로 제거하지 않을 경우 적절한 조치를 취하기 전까지 메모리는 점유된 상태가 된다. 해결방안으로 콜백에 대한 약한 참조weak reference으로 저장하는 방법이 있다.(WeakHashMap)