-
아이템 7. 다 쓴 객체 참조를 해제하라.C 혹은 C++ 언어를 사용해 보았는데 메모리를 직접 관리, 특히 생성 후 해제하는 것이 매우 귀찮고 혹은 해제하는 일을 잊어버리는 경우를 많이 경험했다. GC(Garbage Collection)시스템에서 더 이상 사용하지 않는 동적 할당된 메모리 블럭을 찾아 자동으로 다시 사용 가능한 자원으로 회수하는 것 그리고 자바 시스템에서 GC를 수행하는 부분을 JVM의 Garbage Collector. JVM 메모리 구조Garbage Collector의 타깃이 되는 영역은 Heap 영역. Heap 영역 객체의 메모리 주소를 가지고 있는 참조 변수가 삭제되는 경우 등의 이유로 더 이상 해당 객체에 접근할 수 없는 객체를 Unreachable 객체라 부르고 이러한 객체를 주기적으로 Garbage Collector가 제거해줌. Q) 그런데 Garbage Collector가 있는데 갑자기 '다 쓴 객체 참조를 해제하라' ???? Garbage Collector가 해주는게 아니였나?메모리 관리를 직접 해줘야 하는 경우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());
}
} Q) 해당 코드의 문제점은 ??A) 메모리 누수스택을 사용하는 경우 프로그램을 오래 실행하다 보면 Garbage Collector 활동과 메모리 사용량이 늘어나 결국 성능이 저하가 됨. (심한 경우 디스크 페이징 or OutOfMemoryError 발생하여 프로그램이 종료) 해당 코드의 메모리 누수 위치는 ? --> 스택에서 꺼내진(pop) 객체들을 Garbage Collector가 회수 하지 않음. Q)스택에서 꺼내진 객체들은 더 이상 접근할 수 없는 unreachable 객체라 Garbage Collector가 회수하지 않나???--> 그렇게 함부로 unreachable 객체로 판단해선 안된다. 위 코드에서 아직 element[size] 로 접근이 가능하기 때문에 unreachable 하지 않음. A)위 경우 스택이 해당 객체들의 다 쓴 참조 (obsolete reference)를 가지고 있기 때문
앞의 코드에서는 elements 배열의 '활성 영역' 밖의 참조들이 모두 여기에 해당. GC 언어 (java와 같은) 에서는 사실 의도치 않게 객체를 살려두는 메모리 누수를 찾기 매우 까다로움.객체 참조 하나를 살려두면 Garbage Collector는 그 객체 뿐 아니라 그 객체가 참조하는 모든 객체(그리고 또 그 객체들이 참조하는 모든 객체 ~~~~)를 회수 하지 못함. GC의 동작 과정을 더 자세히 안다면 이해하기 쉽다고 판단된다.GC 동작 과정 <-- 해당 링크를 참고하길 바란다. 해결 방안매우 간단하다. 해당 참조를 다 썼을 때 null 처리 (참조 해제)하면 됨 public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result
} 다 쓴 참조를 null 처리 하게 되었을때 다른 이점
문제점null 처리에 혈안이 되면 프로그램을 필요 이상으로 지저분하게 만듬
obsolete reference 처리 하는 다른 방법 (null 처리 보다 더 좋은 방법)
null 처리를 언제 할까?1. 자기 메모리를 직접 관리하는 클래스에서 비활성 영역이 되는 순간먼저 위의 Stack 클래스가 왜 메모리 누수에 취약한지부터 알아야 한다.
문제점
해결책
2. 캐시의 사용이 필요 없어졌을 때
문제점캐시를 만들때 보통 캐시 엔트리의 유효 기간을 정확히 정의하기 어려움 따라서 언제 참조를 해제 해야할지 알기 어려윰. 해결책이런 경우 쓰지 않는 엔트리를 때때로 청소 해줘야 함.
3. listener (혹은 callback)문제점클라이언트가 콜백을 등록만 하고 명확히 해지하지 않을시 콜백은 계속 쌓여감 해결책콜백을 약한 참조(weak reference)로 저장하면 가비지 컬렉터가 즉시 수거. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 3 replies
-
https://yudeokrin.github.io/posts/2022-10-07-page54 제가 JVM 구조 간략히 설명한 것입니다. 부족한 부분 피드백 해주시면 감사하겠습니다 . |
Beta Was this translation helpful? Give feedback.
-
캐시 내용에서 WeakHashMap이 나오는데, private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> { 이렇게 WeakReference를 상속하고 있네요. 내용을 간략히 정리하자면 WeakReference<Sample> wr = new WeakReference<Sample>( new Sample());
Sample ex = wr.get();
...
ex = null; Sample 인스턴스를 WeakReference로 감싸서 생성하고, Sample 인스턴스에 대한 strong reference를 끊어버리면 Sample 인스턴스는 weak reference를 통해서만 참조하는 형태인 weakly reachable 인스턴스가 될 것이고,, GC에서 weakly reachable 인스턴스로 판단하면 Sample 인스턴스에 대한 참조를 null로 만들어서 (GC 대상으로 만들어서) GC에 의해 회수되도록 한다고 하네요. |
Beta Was this translation helpful? Give feedback.
-
개인적으로는 실무에서 메모리 누수에 대한 부분을 매번 신경 써서 개발하는 것은 이상적이라고 생각되고, 아이템 57을 적극 활용하는 것이 현실적일 것 같습니다. |
Beta Was this translation helpful? Give feedback.
캐시 내용에서 WeakHashMap이 나오는데,
이렇게 WeakReference를 상속하고 있네요.
Reference에 관련된 내용은 여기 참고하면 좋아요 (내용이 상당히 복잡하긴 합니다)
https://d2.naver.com/helloworld/329631
내용을 간략히 정리하자면
Sample 인스턴스를 WeakReference로 감싸서 생성하고, Sample 인스턴스에 대한 strong reference를 끊어버리면 Sample 인스턴스는 weak reference를 통해서만 참조하는 형태인 weakly reachable 인스턴스가 될 것이고,, GC에서 weakly reachable 인스턴스로 판단하면 Sample 인스턴스에 대한 참조를 null로 만들어서 (GC 대상으로 만들어서) GC에 의해 회수되도록 한다고 하네요.