Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

전공공부

아이템 7. 다 쓴 객체 참조를 해제하라 본문

Study/Java

아이템 7. 다 쓴 객체 참조를 해제하라

monitor 2023. 2. 13. 23:42

자바는 Managed Language로써 일반적으로 언어상에서 메모리를 관리해주는 언어이다. 그래서, 메모리를 직접 관리하지 않아도 된다고 착각 할 수 있는데 이는 사실이 아니다.

 

예시 코드
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();
    }

	private void ensureCapacity(){
    	if(elements.length == size)
        	elements = Arrays.copyOf(elements,2*size+1);
    }
}

 

해당 코드는 실제 사용시 별 문제 없이 돌아 갈 것 처럼 보인다.

 

하지만, 이 코드는 메모리 누수가 존재한다.

 

스텍이 다 쓴 참조 객체의 메모리를 소유하고 있기 때문이다.

 

그래서 이를 방지 하기 위해서는 다 쓴 참조 객체를 null로 다시 초기화 진행 시켜주면된다.

 

	public Object pop(){
    	if(size == 0)
        	throw new EmptyStackException();
        Object res = elements[--size];
        elements[--size] = null; // 다 쓴 참조객체 null 처리
        return res;
    }

 

그러나, 위와 같은 상황은 예외적인 상황에서만 사용되어지고 또 다른 좋은 방법은 유효 범위(scope) 바깥으로 참조 객체를 밀어내는 것이다.

 

	public void test(){
    	Object test = 24;    
    }

위와 같이 코드를 짜게 된다면 지역 변수 scope 내부에서만 test 변수가 유효하니 GC에서 알아서 메모리 관리를 해준다.

 

메모리 관리에 유의 해야 할 지점

위 Stack 클래스 처럼 가비지 컬랙터가 사용 종료 여부를 알 수 없고 메모리를 직접 관리하는 클래스이면 사용자가 직접 메모리 누수를 관리하여야 한다.

 

캐시 = 메모리 누수의 주요 원인

 

객체 참조를 캐시에 나두고서 그 객체를 다 쓴 이후에도 한참을 그냥 놔두게 된다면 이 또한 메모리 누스의 원인이다.

 

그러므로 캐시 외부에서 key를 통해서 살아 있는 동안만 엔트리가 살아 있는 캐시가 필요하다면 WeakHashMap을 사용하자

 


WeakHashMap의 이해

WeakHashMap을 이해하고 넘어가려면 아래 개념을 먼저 파악하여야 한다.

  1. 강한 참조 (Strong Reference)
    Integer prime = 1;   와 같은 가장 일반적인 참조 유형이다.   prime 변수 는 값이 1 인 Integer 객체에 대한 강한 참조를가진다.  이 객체를 가리키는 강한 참조가 있는 객체는 GC대상이 되지않는다.
  2. 부드러운 참조 (Soft Reference)
    SoftReference<Integer> soft = new SoftReference<Integer>(prime);   와 같이 SoftReference Class를 이용하여 생성  가능하다.  만약 prime == null 상태가 되어 더이상 원본(최초 생성 시점에 이용 대상이 되었던 Strong Reference) 은 없고 대상을 참조하는 객체가 SoftReference만 존재할 경우 GC대상으로 들어가도록 JVM은 동작한다.  다만 WeakReference 와의 차이점은 메모리가 부족하지 않으면 굳이 GC하지 않는 점이다.
  3. 약한 참조 (Weak Reference)
    WeakReference<Integer> soft = new WeakReference<Integer>(prime);   와 같이 WeakReference Class를 이용하여 생성이 가능하다.  prime == null 되면 (해당 객체를 가리키는 참조가 WeakReference 뿐일 경우) GC 대상이 된다.  앞서 이야기 한 내용과 같이 SoftReference와 차이점은 메모리가 부족하지 않더라도 GC 대상이 된다는 것이다.    다음 GC가 발생하는 시점에 무조건 없어진다.

 

 

 

map1, softReference null을 연속으로 하는 이유

 

import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

public class WeakHashMapTest {
    public static void main(String[] args) {
        WeakHashMap<Integer, String> map = new WeakHashMap<>();
        Map <Integer,String> map1 = new HashMap();
        Integer key1 = 1000;
        Integer key2 = 2000;
        map1.put(key1,"k");
        map1.put(key2,"a");
        SoftReference softReference = new SoftReference<Map>(map1);
        map.put(key1, "test a");
        map.put(key2, "test b");
        key1 = null;
        System.gc();  //강제 Garbage Collection
        System.out.println(softReference.get());
        map1 = null;
        softReference = null;
        System.gc();
        map.entrySet().stream().forEach(el -> System.out.println(el));
    }
}

결과물


결론

메모리 누수는 겉으로 드러나지 않아 시스템에 잠복하기 쉬워 발견하기 힘들다. 따라서 예방법을 숙지하고 상황에 맞게 잘 쓰자.