자바(Java)에서의 LRUCache 메모리릭과 해결방안..

LRU(Least Recently Used)는 페이지를 사용하는 운영체제에서, 새로운 페이지를 할당하기 위해서 마지막에 사용한 페이지를 교체하는 알고리즘이다. LRUCache는 LRU 알고리즘을 이용하는 캐시클래스라고 보면 되겠다. 여기에서는 많이 사용하는 LRUCache 형태를 살펴보고, 이 형태가 가지고 있는 문제(메모리 릭)를 해결해 보자.

1. LRUCache 구현 방법

1.1 LinkedHashMap을 사용해서 만들기

final int MAX_ENTRIES = 100;
Map cache = new LinkedHashMap(MAX_ENTRIES+1, .75F, true) {
    // This method is called just after a new entry has been added
    public boolean removeEldestEntry(Map.Entry eldest) {
        return size() > MAX_ENTRIES;
    }
};

1.2 LinkedHashMap을 상속해서 만들기
대체로 아래의 코드가 많이 보이다. 얼핏 보기에는 잘 동작할 것으로 보인다. 하지만 시간이 지나면서 문제(메모리 릭)가 발생하게 된다.

public class LRUCache<K,V> extends LinkedHashMap<K,V> {
    private int lruSize;

    public LRUCache(int size) {
        lruSize = size;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return size() > lruSize;
    }
}

2. 해결책
해결책을 찾아보니, https://code.google.com/p/reflection-dsl/source/browse/trunk/ReflectionDsl/src/br/com/bit/ideias/reflection/cache/LRUCache.java?spec=svn204&r=204 에서는 Value에 해당하는 객체를 SoftReference(참고로, 이넘으로 객체를 래핑하게 되면 풀 GC가 읽어날때 GC대상이 된다)로 래핑해서 GC에게 위임(?)하고 있네요..

개인적으로는 명시적으로 객체를 삭제해서 메모리릭을 방지하는게 좋다고 생각한다. 잠재적인 메모리릭을 방지해 주는 코드는 아래에서 볼 수 있다.

protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
	boolean isRemove = size() > maxsize;

	if (isRemove) {
		Object obj = this.get(eldest.getKey());  
		if(obj instanceof BufferedWriter) { // V가 BufferedWriter 인스턴스임.. 
			try {
				((BufferedWriter)obj).close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		this.remove(eldest.getKey());
	}

	return isRemove;
}

이상, 자바에서 LRU 형태의 캐시클래스를 만들 때 주의가 필요한 내용을 살펴봤다.

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.