ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [CaffeineCache] "RemovalListener 는 GC가 실행된 이후 호출된다."
    🩸 삽질의 추억 2024. 5. 15. 02:04
    728x90

    상황:

    RemovalListener 가 NullpointerException을 뱉음

    2024-05-14T09:09:09.130Z WARN 1 --- [] [onPool-worker-6] c.g.b.caffeine.cache.BoundedLocalCache : Exception thrown by removal listener

    java.lang.NullPointerException: Cannot invoke "java.util.Queue.size()" because "messageQueue" is null

     

        @Bean
        public Cache<String, ConcurrentLinkedQueue<Message>> chatCache() {
    
            RemovalListener<String, ConcurrentLinkedQueue<Message>> listener = (String key, ConcurrentLinkedQueue<Message> queue, RemovalCause cause) -> {
                if (cause.wasEvicted()) {
                    commitMessageQueue(queue);
                }
            };
    
            return Caffeine.newBuilder()
                    .initialCapacity(200) 
                    .softValues() 
                    .maximumSize(1000)
                    .removalListener(listener)
                    .build();
        }
    
        public void commitMessageQueue(Queue<Message> messageQueue) {
            int size = messageQueue.size();
            List<Message> messages = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                messages.add(messageQueue.poll());
            }
            bulkInsertMessages(messages);
        }

     

    RemovalListener 를 통해 <K, V> 중 Value (내 경우 Queue.size()) 를 불러왔는데 null 값이라 NullPointerException 이 남.
    좀 더 자세히 설명하자면 RemovalListener 로 Remove 실행되는 객체를 저장하려고 했음.

     

     
    삽질한 이유:
    RemovalListener 가 GC 실행 전 - 그러니까, 서버 꺼지기전 실행되는 destroy() 마냥 - 감지해서 먼저 실행하는 메소드 인줄 알았음. 그래서 나는 캐시가 삭제되기전에 캐시 내용을 디비에 옮길 목적으로 코드를 짰다.
     
     
    설명:
    다양한 상황에 의해 messageQueue 객체가 가비지 컬렉터에 의해 예기치 않게 회수될 수 있다. 이 때 가비지컬렉터는 참조하는 객체를 해제한다.  RemovalListener가 호출되더라도, 이미 회수된 객체에 대한 조작은 NullPointerException을 발생시킬 수 있다.
    또한, 힙이 부족할때만 GC 가 실행되는게 아니라는 것을 알게됨 <- 이부분 공부하기
     
     
    나는 내가 따로 캐시를 관리할것이기 때문에 & 캐시가 함부로 삭제되면 안되기 때문에.softValue() 를 지운다.
    이 소프트 레퍼런스는 메모리가 부족할 때 가비지 컬렉터에 의해 회수될 수 있다.
    또한 RemovalListener 도 지운다..

     

    //

    약한 참조나 부드러운 참조를 사용하지 않으면, 캐시된 모든 항목은 강한 참조(strong reference)를 유지합니다. 이는 캐시 자체가 참조되는 한, 모든 항목이 메모리에 남아 있음을 의미하며, 캐시가 너무 커질 경우 메모리 사용량이 증가하고 OutOfMemoryError가 발생할 위험이 있습니다.

    728x90
Designed by Tistory.