Ehcache 가 메모리에서 사라지지 않는다

나르·2023년 3월 15일
0

트러블슈팅

목록 보기
5/5
post-thumbnail

Issue

서버가 CPU를 40% 이상 지속적으로 사용하고 있었습니다. 별다른 일을 하지는 않는데 왜일까요?

원인

full gc가 짧은 주기로 실행되고 있으며, gc 이후에도 메모리 해제가 거의 되고 있지 않고 있었습니다.

덤프를 떠서 확인해보니 이미 expire 된 ehcache element 들이 gc 이후에도 메모리에 존재하고있습니다.
왜 gc 대상 되지 않고 계속 살아있는 걸까요...🥲

Ehcache : Pinning, Expiration, and Eviction

ehcache 는 기본적으로 캐시 성능을 위해, expire 된 element를 바로 지우지 않습니다.
실제 gc 대상이 되려면 evict가 되어야 하는데, evict 트리거는 캐시 용량이 full 이어서 더 이상 캐시에 데이터 저장을 하지 못할 때, 미리 설정한 evict 알고리즘 (LRU ,LFU 등)에 의해 evict가 실행됩니다.
이 때, 비로소 해당 element의 객체가 unreachable 상태가 되어 gc 대상이 됩니다.

++ ehcache does not remove Element from memory on eviction

In Ehcache, expired cache elements are not immediately removed from memory. This is because Ehcache uses a passive expiration strategy, which means that it relies on the application to access or remove the expired elements.
When an element in the cache expires, it becomes eligible for eviction, but it remains in memory until it is either accessed or explicitly removed. This is because Ehcache does not actively check for expired elements or remove them from memory.
Therefore, if your application does not access or remove expired elements, they will continue to occupy memory until the cache reaches its maximum size and eviction occurs.
To avoid this situation, you can use a more aggressive expiration strategy, such as time-to-live (TTL) or time-to-idle (TTI), which actively check for expired elements and remove them from the cache. Alternatively, you can manually remove expired elements from the cache using the remove method.
It's also worth noting that Ehcache provides a feature called "on-heap caching" where the cache data can be stored directly on the Java heap. In this case, the expired cache elements will be removed during the next GC cycle of the JVM, which might not happen immediately. So, if you need to free up memory immediately, you can use the remove method or consider using off-heap or disk storage for your cache

Solution

일단 로컬 캐시의 Max 설정을 줄여 full gc 가 발생하지 않게 했습니다.
또한 expired 된 캐시는 다시 호출될 때도 evict 처리되는데 남아있다는 것은 히트율이 떨어지는 것이라 판단해 캐시 구조도 변경했습니다!

(IMHO)
이번 일을 겪으니 왜 로컬캐시로 caffeine 대신 ehcache 를 쓰는지 더 알 수 없어졌습니다🤔
캐시 클러스터링을 하는 것도 아니고 off-heap을 쓰는 것도 아니고... 테스트했을 때 성능도 caffeine 이 더 좋게 나왔는데 왜 ehcache 를 쓸까요?
흠믐므...내일 팀장님한테 물어봐야겠습니다

ehcache does not remove Element from memory on eviction


물어본 결과...
프로젝트 개발 당시에는 caffeine 이 Spring 지원 캐시가 아니었고, ehcache 는 스프링 지원 캐시여서 Ehcache 를 썻다고 합니다.
하지만 현재는 Caffeine 도 스프링에서 지원하고 있고, 외에도 몇가지 이유가 있었지만 전부 Caffeine에서도 지원하고 있는 기능인 것으로 확인해 변경하기로 했습니다!

그리고 결론은 선택적으로 Ehcache 또는 Caffeine 를 적용할 수 있는 CacheManager을 만들게 되었습니다...
만드는건 좋긴 한데 물흐르듯 일감이 늘었네요🙄

profile
💻 + ☕ = </>

0개의 댓글