[Item7] 다 쓴 객체 참조를 해제하라

Sera Lee·2022년 2월 21일
0

EffactiveJava

목록 보기
6/9
post-thumbnail

메모리 누수 막기

해당 참조를 다 썼을 때 null 처리(참조해제)

  • 나쁜 예제
    • 책에서 pop() 을 실행했을 때 Size를 줄여도 Array는 그대로이다.
    • 즉 참조는 그대로 이기 때문에 Object는 살아있는 상태이다.
public Object pop() {
	if(size == 0) {
		throw new EmptystackException();
	}
// 반환만 하고 참조를 해제하지 않음
	return elements[--size];
}
public Object pop() {
	if(size == 0) {
		throw new EmptystackException();
	}
	Object result = elements[--size];
	elements[size] = null;
	return result;
}
  • 이점

    • 메모리 누수를 막을 수 있을 뿐 아니라,
    • 만약 null 처리 한 참조를 실수로 사용하려 하면 프로그램은 즉시 `NullPointerException 을 던지며 종료된다.
      • 그냥 처리된 경우 오동작을 할 수 있음을 막는다.
  • 하지만, 이 방법은 이상적이지 않다.

    • 다 쓴 객체를 해제하는 좋은 방법은 참조를 담은 변수를 유효 scop 밖으로 밀어내는 것이다.
    • 즉, 변수의 범위를 최소화하게 정의하라
  • Stack 클래스는 왜 메모리 누수에 취약한 걸까?

    • 스택은 elements Array 로 저장소 풀을 만들어 원소를 관리하기 때문이다.
    • 가비지 컬렉터는 비활성영역과 활성영역을 구분 할 수 없고, 비활성 영역 개체도 모두 유효한 객체로 인식되기 때문에 수거해 가지 않는다.
    • 프로그래머만 비활성역역 활성영역을 구분할 수 있기 때문에, 비활성영역이 되면 null 처리 하여, 가비지컬렉터가 회수할 수 있도록 한다.

캐시 관리

캐시는 리소스를 디스크에서 로드하는 것은 비싸므로 자주 사용하는 리소스를 메모리에 두고 사용하고자 할 때 사용된다.

하지만 캐시에 올린 리소스를 참조하는 곳이 없는 경우, 리소스를 제거를 해야하는데, 강력한 참조를 사용하면 해당 참조 자체로 리소스에 메모리에 남아있게 된다.

GC가 리소스가 필요치 않아지는 시점을 판별하고 캐시에서 리소스를 제거 해야한다.

WeakHashMap

  • WeakHashMap 은 오래 사용되거나 사용되지 않늰다고 판단되는 객체를 GC가 자동으로 제거한다.
public class WeakHashMapTest {

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

결과

2000=test b
Process finished with exit code 0

null 로 할당된 key1 이 Map 내에서 제거되었다.

LinkedHashMap

  • 일반적인 Map은 순서가 없이 저장된다.
  • LinkedhashMap 은 저장한 순서대로 값이 들어가 있고, removeEldestEntity() 라는 특수한 기능이 있다.

removeEldestEntity()

  • 저장되는 특정 기준값보다 가장 마지막에 들어온 값을 삭제한다.

리스너, 콜백 관리

  • 콜백을 등록하고, 해지하지 않으면 콜백은 계속 쌓인다.
  • 이때 콜백을 약한 참조로 저장하면 GC가 즉시 수거해간다.
Map<String,Object> linkedMap = new LinkedHashMap<String,Object>() {

	// 가장 오래된 값 삭제
  @Ovrride
	public boolean removeEldestEntry(Map.Entry<K, V> eldest) {
		return this.size() == MAX_ENTRIES ? true : false;
	}
};

참조
https://meaownworld.tistory.com/77

0개의 댓글