Статический Map как кэш — рабочий код до утечки памяти
Паттерн встречается в проде чаще, чем хотелось бы: заводят приватное статическое поле, кладут туда значения по ключу и называют это кэшем.
public class PriceService {
private static final Map CACHE = new HashMap<>();
public BigDecimal getPrice(String sku) { return CACHE.computeIfAbsent( sku, this::loadFromDb); } }
В тестах и на старте прода всё работает: ключей мало, память не заканчивается. Проблема появляется, когда ключи — это не фиксированный список SKU, а что-то растущее без границы: id пользователя, токен сессии, комбинация параметров запроса.
Map растёт, ничего не вытесняется, объекты в значениях живут в Old Gen до конца жизни процесса. GC-паузы постепенно растут, потому что сборщик честно проходит по всё большему живому графу объектов, пока не долетает до OutOfMemoryError или до перезапуска пода по памяти.
На ревью в такой момент спросят про eviction policy — maximumSize, expireAfterAccess, expireAfterWrite. Ожидают Caffeine или Guava Cache вместо голого HashMap. Если предложить WeakHashMap как решение — это не то же самое: там очищается запись, только если сам объект-ключ не удерживается больше нигде, а не по политике размера или времени.
Кэш без границы — это не кэш, это утечка с отложенным сроком.
Тренажёр: 600 вопросов, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки