transient sclass ThumbnailCache { gettable WidthAndHeightImpl thumbnailSize = new(100); // max bytes in memory settable long maxBytes; // estimated bytes in memory gettable long totalBytes; new TheMap map; transient Lock lock = lock(); class TheMap extends LinkedHashMap { @Override protected bool removeEldestEntry(Map.Entry eldest) { if (totalBytes > maxBytes) { totalBytes -= guessImageBytes(eldest.getValue()); true; } false; } } int guessImageBytes(BufferedImage image) { // 24 is a random guess for the overhead of an image ret 24+guessImageSizeInMemory(image); } BufferedImage makeThumbnail(File file) { ret loadImage2(file); } selfType thumbnailSize(int wAndH) { ret thumbnailSize(new WidthAndHeightImpl(wAndH)); } selfType thumbnailSize(WidthAndHeightImpl size) { lock lock; if (eq(thumbnailSize, size)) this; thumbnailSize = size; map.clear(); this; } synchronized BufferedImage get(File imageFile) { lock lock; if (map.containsKey(imageFile)) ret map.get(imageFile); var img = makeThumbnail(imageFile); totalBytes += guessImageBytes(img); map.put(imageFile, img); ret img; } }