C 나 C++ 처럼 손수 메모리 관리를 해야하는 언어와 달리 Java에서는 JVM 에서 GC(Garbage collertion : 쓰레기 수집가) 를 이용하여 안쓰는 객체를 지우는 기능(쓰레기 수집) 이 가능하지만 메모리 관리를 하지 않으면 '메모리 누수' 의 문제가 생긴다. 그 결과로 GC 가 할일이 많아져 성능이 저하되거나 메모리 요구량이 증가한다.
극단적으로는 디스크 페이징 (Disk paging), OutOfMemortError 가 던져지면서 프로그램이 중단될 것이다.
1. 메모리 누수는 어디서?
스택이 커졌다가 줄어들면서 제거한 객체들을 쓰레기 수집기가 처리하지 못할때, 가용범위를 벗어난 만기 참조(obsolete refrence) 를 그대로 가지고 있을때
* 만기 참조 : 다시 이용되지 않을 참조를 말한다.
[ Stack 예시 ]
data를 pop 할시 가용범위(현재 사이즈(index)를 벗어난 범위는 다시 사용할 일이 없지만 레퍼런스 정보는 그대로 남겨진다.
public class Stack {
private Object[] elements;
private int index = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack(){
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e){
ensureCapacity();
elements[index++] = e;
}
public Object pop(){
if(index ==0)
throw new EmptyStackException();
Object result = this.elements[--index];
return result;
}
/**
* 적어도 하나 이상의 원소를 담을 공간을 보장한다.
* 배열의 길이를 늘려야 할때 대략 두배씩 늘인다.
* */
private void ensureCapacity(){
if(elements.length == index)
elements = Arrays.copyOf(elements, 2*index+1);
}
}
2. 만기 참조를 보유한다는것은 어떤 문제가 생길까?
그 객체를 뿐만 아니라 의도치 않은 객체보유(unintentional object retention)를 통해서 참조되는 다른 객체들도 함께 쓰레기 수집에서 제외될 가능성이 높아, 만기참조가 단 몇개라도 굉장이 많은 객체가 함께 제외될 가능성이 있다.
3. 해결 방법
이 문제를 해결하기 위해선 가용범위를 벗어난 즉 사용되지 않을 객체에 대한 참조를 폐기해주는 조치가 필요하다.
쓸일 없는 객체 참조는 무조건 null 로 만든다. Stack 클래스의 경우, 스택에서 pop 된 객체에 대한 참조는 그 즉시 null 로 만들면 된다.
public Object pop(){
if(index ==0)
throw new EmptyStackException();
Object result = this.elements[--index];
this.elements[index] = null; // 만기 참조 제거 부분
return result;
}
4.주의 할점은 ?
무조건적으로 Null 로 처리하는것은 코드를 난잡하게 만들 수 있다. 객체 참조를 null 처리 하는것은 규범(norm) 이라기 보다는 예외적인 조치가 되어야 한다. 만기 참조를 제거하는 가장 좋은 방법은 해당 참조가 보관된 변수가 유효 범위(scope) 를 벗어나게 두는것이다. 변수를 정의할 때 그 유효 범위를 최대한 좁게 만들면 null 처리 할 필요 없이 자연스럽게 처리된다.
일반적으로 자체적으로 관리하는 메모리가 있는 클래스를 만들때는 메모리 누수가 발생하지 않도록 주의해야한다.
위 예제에서는 elements 배열이 이 클래스에 해당된다. ( 함수 내에 로컬 선언된 변수는 쓰레기 수집기에 의해 제거된다 -> 해당 범위를 벗어나면 필요 없다고 여겨지기 때문이다. )
5. 언제 사용하는가?
주로 메모리 누수가 흔히 발생하는 장소에서 사용된다.
1. 캐시(cache)
캐시는 주로 Map 으로 구현된다. 이경우 HashMap 을 WeakHashMap 으로 변경하여 구현할 수 있다.
일반적인 HashMap의 경우 일단 Map안에 Key와 Value가 put되면 해당 엔트리는 사용 여부와 관계 없이 삭제가 되지 않으며 프로그래머가 전적으로 삭제하여 관리하여야 한다고 설명한다.
WeakHashMap을 사용할경 우 put된 key 값을 weakReference 가 감싸고 들어가 만약 해당 키가 범위(scope)를 벗어난다면 HashMap의 Element를 자동으로 제거, GC 해버린다. Key에 해당하는 객체가 더이상 사용되지 않는다고 판단되면 제거한다는 의미이다. (아래 블로그 글 참조)
blog.breakingthat.com/2018/08/26/java-collection-map-weakhashmap/
Java - Collection - Map - WeakHashMap (약한 참조 해시맵) - 조금 늦은, IT 관습 넘기 (JS.Kim)
Java - Collection - Map - WeakHashMap (약한 참조 해시맵) 강한 참조 (Strong Reference) 부드러운 참조 (Soft Reference) 약한 참조 (Weak Reference)
blog.breakingthat.com
+ weak reference 에 대한 정리
Weak reference의 이해
Weak reference (약한 참조)대한 글을 시작해 보려고 합니다. 먼저 자바(JVM)의 메모리를 구조를 이해해야 weak reference, soft reference, phantom reference를 보다 잘 이해 할 수 있습니다. JVM에서 자바의 메..
tourspace.tistory.com
2. 리스너(listener) 등의 역호출자(callback)
'java' 카테고리의 다른 글
[자바 병렬 처리 - 포크/ 조인 프레임 워크] (0) | 2021.04.17 |
---|---|
[JAVA]스트림(Stream) 에서 기본형(Primitive type)은 왜 취약할까? (0) | 2021.04.13 |
[JAVA 개념] 추상클래스와 인터페이스의 차이점 ( + 상속과 구현) (0) | 2021.04.11 |
[Effective java] Item 7 - 종료자(finalize) 사용을 피하라 (0) | 2021.01.30 |
[디자인패턴] 빌더(Builder) - 개념 정리,예제 (0) | 2021.01.28 |