Automatically frees the low-priority-cache when memory runs out.

This commit is contained in:
Stian Grenborgen 2023-12-10 14:53:05 +01:00
parent 91a8055e2f
commit da05dbdb5e
1 changed files with 55 additions and 5 deletions

View File

@ -21,6 +21,8 @@ package net.sf.freecol.common.resources;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
@ -37,13 +39,15 @@ public class ImageCache {
private static final Logger logger = Logger.getLogger(ImageCache.class.getName());
private static final boolean DEBUG_PRINT_CACHE_SIZES_TO_STDOUT = false;
private static final boolean DEBUG_PRINT_CACHE_SIZES_TO_STDOUT = true;
public static final String REPLACEMENT_IMAGE = "image.miscicon.delete";
/** A cache of scaled (and possibly greyed) images. */
private final Map<Long, BufferedImage> cache;
private final Map<Long, BufferedImage> lowPriorityCache;
private final Map<Long, CacheSoftReference<Long, BufferedImage>> lowPriorityCache;
private final ReferenceQueue<BufferedImage> lowPriorityCacheReferenceQueue = new ReferenceQueue<>();
private final CacheCleanupThread cleanupThread = new CacheCleanupThread();
private long cacheSize = 0;
private long lowPriorityCacheSize = 0;
@ -55,6 +59,8 @@ public class ImageCache {
public ImageCache() {
this.cache = new HashMap<>();
this.lowPriorityCache = new HashMap<>();
cleanupThread.start();
}
@ -167,13 +173,18 @@ public class ImageCache {
if (image != null) {
return image;
}
image = this.lowPriorityCache.get(cacheKey);
image = getFromLowPriorityCache(cacheKey);
if (image != null) {
return image;
}
return null;
}
private BufferedImage getFromLowPriorityCache(long cacheKey) {
final SoftReference<BufferedImage> ref = this.lowPriorityCache.get(cacheKey);
return (ref != null) ? ref.get() : null;
}
private void placeImageInCache(long hashKey, BufferedImage image) {
if (image != null) {
this.cache.put(hashKey, image);
@ -184,8 +195,10 @@ public class ImageCache {
private void placeImageInLowPriorityCache(long hashKey, BufferedImage image) {
if (image != null) {
this.lowPriorityCache.put(hashKey, image);
lowPriorityCacheSize += image.getWidth() * image.getHeight() * 4;
final long size = image.getWidth() * image.getHeight() * 4;
final CacheSoftReference<Long, BufferedImage> ref = new CacheSoftReference<>(hashKey, size, image, lowPriorityCacheReferenceQueue);
this.lowPriorityCache.put(hashKey, ref);
lowPriorityCacheSize += size;
debugPrintCacheSizes();
}
}
@ -273,4 +286,41 @@ public class ImageCache {
this.lowPriorityCache.clear();
this.lowPriorityCacheSize = 0;
}
private final class CacheCleanupThread extends Thread {
@Override
public void run() {
try {
while (true) {
@SuppressWarnings("unchecked")
final CacheSoftReference<Long,BufferedImage> ref = (CacheSoftReference<Long,BufferedImage>) lowPriorityCacheReferenceQueue.remove();
if (ref == null) {
continue;
}
lowPriorityCacheSize -= ref.getSize();
lowPriorityCache.remove(ref.getKey());
}
} catch (InterruptedException e) {}
}
}
private final class CacheSoftReference<K, V> extends SoftReference<V> {
private final K key;
private final long size;
public CacheSoftReference(K key, long size, V referent, ReferenceQueue<? super V> q) {
super(referent, q);
this.key = key;
this.size = size;
}
public K getKey() {
return key;
}
public long getSize() {
return size;
}
}
}