@VisibleForTesting synchronized void deleteUnreferenced(Clock clock) { // Keep track of snapshots to delete to avoid CME. Set<T> toDelete = new LinkedHashSet<>(); for (Map.Entry<T, SnapshotState> entry : snapshotStates.entrySet()) { if (entry.getValue().streamCount == 0 && entry.getValue().lastSeen.isBefore( clock.instant().minus(collectAfterMillis, ChronoUnit.MILLIS))) { // clearSnapshot will do nothing and return false if there are any pending watches - this // ensures that we don't actually remove a snapshot that's in use. T groupIdentifier = entry.getKey(); if (snapshotCache.clearSnapshot(groupIdentifier)) { toDelete.add(groupIdentifier); } } } toDelete.forEach(group -> { snapshotStates.remove(group); collectorCallbacks.forEach(cb -> cb.accept(group)); }); }