1. GC의 메모리 누수 : null로 해제
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);
}
// // 코드 7-2 제대로 구현한 pop 메서드 (37쪽)
// public Object pop() {
// if (size == 0)
// throw new EmptyStackException();
// Object result = elements[--size];
// elements[size] = null; // 다 쓴 참조 해제
// return result;
// }
public static void main(String[] args) {
Stack stack = new Stack();
for (String arg : args)
stack.push(arg);
while (true)
System.err.println(stack.pop());
}
}
메모리 누수 문제 = 가비지 컬렉션 활동과 메모리 사용량이 늘어나 성능이 저하된다.
스택에서 pop()이 된 객체를 GC가 회수하지 않는다. 프로그램에서 그 객체를 더이상 사용하지 않아도! (다 쓴 참조를 여전히 갖고있어서)
GC의 메모리 누수 : 객체 참조 하나를 살두면, 이 객체뿐 아니라, 이 객체가 참조하는 모든 객체를 회수하지 못한다.
solution : 해당 참조를 다 썼을 때 null 처리한다.
2. Null 처리는 예외적이다.
객체 참조를 Null 처리하는 일은 예외적인 경우여야한다.참조를 담은 변수를 유효 범위(scope) 밖으로 밀어낸다.
- 스택은 자기 메모리를 직접 관리한다.
- 배열의 활성영역에 속한 원소만 사용되고 비활성 영역은 쓰이지 않는다. (GC가 알 방법이 없다.)
- 비활성 영역이 되는 순간 Null 처리로 GC에게 알릴 때 사용한다.
3. 메모리 누수의 주범
a. 자기 메모리를 직접 관리하는 클래스
프로그래머가 항시 메모리 누수에 주의한다. (ex stack)
b. 캐시
객체 참조를 캐시에 넣고, 객체를 다 쓴 후에도 놔누는 경우
해결책
- WeakHashMap을 사용해 캐시를 만든다. -> 외부에서 키를 참조하는 동안만 엔트리가 살아있는 캐시
- 시간이 지날수록 엔트리 가치를 떨어뜨리는 방식 ->ScheduledThreadPoolExecutor (쓰지 않는 엔트리 청소)
c. 리스너(listener), 콜백(callback)
클라이언트가 콜백을 등록만하고 해지하지 않는경우, 계속 쌓인다.
해결책
- 콜백을 약한 참조(weak reference)로 저장한다. -> WeakHashMaP에 키로 저장한다.
4. 참고자료
https://d2.naver.com/helloworld/329631
java.lang.ref 패키지의 객체 참조 종류 4가지
- strong reference
- soft reference
- weak reference
- phantom reference
LRU(least recently used) 캐시 같은 애플리케이션 쉽게 작성가능
GC의 reachability
reachable : 어떤 객체에 유효한 참조가 있다. (root set : 유효한 최초의 참조)
unreachable : 어떤 객체에 유효한 참조가 없다. (GC 대상)
soft, weak, phantom reference
- reference Object
SoftReference, WeakReference, PhantomReference 3가지 클래스에서 생성된 객체
- WeakReference, Weak Reachable
WeakReference<Sample> wr = new WeakReference<Sample>(new Sample());
Sample ex = wr.get(); // 참조되었다.
ex = null; // weakly reachable 객체
GC 동작마다 회수된다. LRU 캐시 구성시 주로 WeakReference를 사용한다.
WeakReferencerence 객체내의 weakly reachable 객체에 대한 참조가 null로 설정되면, GC에 의해 메모리 회수
- SoftReference, Softly Reachable
Strong reachable이 아니면서 SoftReference 객체로만 참도된 객체자주 사용될수록 더 오래 살아남는다.
(마지막 strong reference가 GC된 때로부터 지금까지의 시간) > (옵션 설정 값 N) * (힙에 남아있는 메모리 크기)
SoftReferencerence 객체내의 softly reachable 객체에 대한 참조가 null로 설정되면, GC에 의해 메모리 회수
- PhantomReference, Phantomly Reachable
GC 대상 객체를 처리하는 작업 (= 객체의 파이널 라이즈 작업) 이후에 GC 알고리즘 따라 메모리 회수한다.
Softly Weakly : GC 대상 여부를 판별Phantomly : 파이널라이즈 이후 작업을 수행하게 한다. Phatom reference를 reference queue에 넣음
파이널라이즈 이후 처리해야하는 리소스 정리 등 작업에서 유용하게 사용할 수 있다. (거의 사용사지 않는다.)
Reference Queue
WeakHashMap 클래스 : Reference Queue + WeakReference 를 사용해 구현되어있다.
Phantom Reference는 참조된 객체를 phantomly reachable로 만들고 eunque한다.
ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
PhantomReference<Object> pr = new PhantomReference<Object>(referent, rq);
'Dev Book Review > Effective Java' 카테고리의 다른 글
[Effective Java] item9. try-finally 보다는 try-with-resources를 사용하라 (0) | 2020.04.15 |
---|---|
[Effective Java] item8. finalizer와 cleaner 사용을 피해라 (0) | 2020.04.15 |
[Effective Java] item6. 불필요한 객체 생성을 피하라 (0) | 2020.04.15 |
[Effective Java] item5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2020.04.15 |
[Effective Java] item4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2020.04.15 |