어떤 객체에 대한 레퍼런스가 남아있다면 해당 객체는 가비지 컬렉션의 대상이 되지 않는다.
자기 메모리를 직접 관리하는 클래스라면 메모리 누수에 주의해야 한다.
참조 객체를 null 처리하는 일은 예외적인 경우이며 가장 좋은 방법은 유효 범위 밖으로 밀어내는 것이다.
// Stack의 코드 7-2 제대로 구현한 pop 메서드 (37쪽)
// outofmemoryException 을 주의하라
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 다 쓴 참조 해제
return result;
}
// 해당 key 가 참조가 되지 않으면 key, value 값을 빼주는 map
public class PostRepository {
private Map<CacheKey, Post> cache;
public PostRepository() {
this.cache = new **WeakHashMap**<>();
}
public Post getPostById(CacheKey key) {
if (cache.containsKey(key)) {
return cache.get(key);
} else {
// TODO DB에서 읽어오거나 REST API를 통해 읽어올 수 있습니다.
Post post = new Post();
cache.put(key, post);
return post;
}
}
public Map<CacheKey, Post> getCache() {
return cache;
}
}
@Test
void cache() throws InterruptedException {
PostRepository postRepository = new PostRepository();
CacheKey key1 = new CacheKey(1);
postRepository.getPostById(key1);
assertFalse(postRepository.getCache().isEmpty());
// wakHashmap 에 의해 gc 에 의해 처리된다.
key1 = null;
// TODO run gc
System.out.println("run gc");
System.gc();
System.out.println("wait");
Thread.sleep(3000L);
assertTrue(postRepository.getCache().isEmpty());
}
스레드를 사용하여 주기적으로 클린업을 하는 작업을 실행
@Test
void backgroundThread() throws InterruptedException {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
PostRepository postRepository = new PostRepository();
CacheKey key1 = new CacheKey(1);
postRepository.getPostById(key1);
Runnable removeOldCache = () -> {
System.out.println("running removeOldCache task");
Map<CacheKey, Post> cache = postRepository.getCache();
Set<CacheKey> cacheKeys = cache.keySet();
Optional<CacheKey> key = cacheKeys.stream().min(Comparator.comparing(CacheKey::getCreated));
key.ifPresent((k) -> {
System.out.println("removing " + k);
cache.remove(k);
});
};
System.out.println("The time is : " + new Date());
executor.scheduleAtFixedRate(removeOldCache,
1, 3, TimeUnit.SECONDS);
Thread.sleep(20000L);
executor.shutdown();
}
p37, NullPointerException
p38, WeakHashMap
p38, 약한 참조 (weak reference)
p39, 백그라운드 쓰레드
p39, ScheduledThreadPoolExecutor
NullPointerException을 만나는 이유
메소드에서 적절한 값을 리턴할 수 없는 경우에 선택할 수 있는 대안