개발하는 두더지

[Effective Java 규칙6] 유효기간이 지난 객체 참조는 폐기해야 한다 본문

Java,Android

[Effective Java 규칙6] 유효기간이 지난 객체 참조는 폐기해야 한다

덜지 2018. 9. 4. 18:53

Effective Java 2/E 책과 구글링을 통해 내용을 정리하고 개인적인 견해가 포함된 글입니다.


C/C++ 처럼 손수 메모리를 관리해야하는 언어와 달리 자바는 GC에 의해 자동으로 적절한 시기에 안쓰는 메모리를 해제한다

하지만 자바도 메모리릭이 분명 발생하며 그 결과로 GC가 할일이 많아져 성능이 저하되고 메모리 요구량이 증가할 것이다

극단적으로 디스크 페이징이 발생하고 OOM(Out Of Memory)가 출력되며 프로그램이 중단될 것이다

public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_SIZE = 4;

public Stack() {
elements = new Object[DEFAULT_SIZE];
}

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가 처리하지 못하므로 메모리릭이 발생한다.


 1

2

3

 4

 5

 6

 

 


이 상태에서 pop()을 호출하면 6이란 값이 반환되지만 Stack 클래스 내부에서는 객체를 해제한 것이 아닌 배열 포인터만 이동시켰다.

즉, element[5] 는 6이란 값이 들어있어서 참조상태이며 아직 사용안하고있는 상태( 클래스가 소멸되지 않는 한 언젠간 사용될 가능성이 높은 상태로 판단)이기 때문에 GC 수집에 포함되지 않는다.


프로그래머 입장에서는 0~4 인덱스까지가 실제 사용될 영역이고 5 인덱스는 프로그램 상 절대 사용될 일이 없는 값이다

하지만 GC는 그것을 알 수가 없다. 이렇게 실수로 객체 참조가 유지되는 경우 해당 객체만 GC에서 제외되는 것이 아닌 그 객체를 통해 참조되는 다른 객체들까지 모두 GC 수집에서 제외된다. 이게 많으면 많을 수록 성능에 큰 영향을 끼칠 것으로 보인다.


간단하게 pop된 객체는 null로 만드는 것이다. 

public Object pop() {
if(size == 0)
throw new EmptyStackException();
Object results = elements[--size];
elements[size] = null;
return results;
}


캐시에서도 메모리 누수가 흔히 발생한다.

객체 참조를 캐시 안에 넣고 잃어버리는 일이 많은데 이를 해결하기 위해서 WeakHashMap을 사용하는 것이다. 즉, 캐시 바깥에서 키를 참조하고 있을 때만 값을 보관하면 될 때 쓸 수 있는 전략이다. 주의해야할 점은 WeakHashMap은 캐시 안에 보관되는 항목의 수명이 키에 대한 외부 참조의 수명에 따라 결정되는 상황에만 적용 가능하다는 것을 기억하자. (무슨말??, WeakHashMap과 이 내용을 찾아보자)


일반적으로 캐시에 보관되는 항목의 수명은 캐시에 보관된 기간에 따라 결정되는 것이 보통이다. 캐시를 정리하는 방법에는 LRU 기법이 있다. 


인터페이스 콜백 리스너를 만들 때  리스너를 set해주고 명시적으로 해제하지 않으면 메모리에 점유된 상태로 유지된다.

Weak reference 약한 참조를하면 콜백을 즉시 처리하는 가장 좋은 방법이다

Weak reference는 reference count가 0이기 때문에 GC의 수집 대상이며 사용할 때 항상 null 체크를 해야한다



정리해보면,

자체적으로 관리하는 메모리가 있는 클래스를 만들 때 메모리 누수가 발생하지 않도록 주의해야 한다.



* 의견 1

다른 진영에서는 GC에서 하는 일을 신경쓰는 것 보다 코드에 집중해라. 이런 의견도 있음.


* 의견 2

안드로이드의 경우 라이프사이클이 짧고 객체를 생성하고 삭제하는 주기가 짧기때문에 자원을 그렇게 신경쓰지 않아도 되는게 아닌가?

주기가 짧아서 객체의 소멸이 발생하기때문에 GC의 대상에 자연스럽게 올라갈 것이므로 객체의 생성과 소멸에 크게 신경안써도 될 것 같다.

예를들면 rx와 같은 함수형 프로그래밍에서는 저자가 다시는 객체의 생성과 소멸에 신경을 쓰는 언어를 쓰고싶지 않다.라고 한다.

그리고 스터디하면서 자바가 객체의 생성과 소멸을 신경쓰면 C C++ 처럼 저수준 언어로 사용하는 것이며 자바 GC의 장점을 버리는 행위 같다





























Comments