본문 바로가기

java

[Effective Java] Item 6 - 유효기간이 지난 객체 참조는 폐기하라.

 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);
    }
}

 

 

Stack 에서의 만기참조 객체 : index가 1일때 Element[2],Element[3] 

 

 

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 에 대한 정리

 

tourspace.tistory.com/37

 

Weak reference의 이해

Weak reference (약한 참조)대한 글을 시작해 보려고 합니다. 먼저 자바(JVM)의 메모리를 구조를 이해해야 weak reference, soft reference, phantom reference를 보다 잘 이해 할 수 있습니다. JVM에서 자바의 메..

tourspace.tistory.com

 

 

2. 리스너(listener) 등의 역호출자(callback) 

반응형