transient sclass ThumbnailCache { settable WidthAndHeightImpl thumbnailSize = new(100); // max bytes in memory settable long maxBytes = oneMegabyte(); // estimated bytes in memory gettable long usedBytes; Q q = startQ(); new TheMap map; transient Lock lock = lock(); class TheMap extends LinkedHashMap { @Override protected bool removeEldestEntry(Map.Entry eldest) { if (usedBytes > maxBytes) { usedBytes -= 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) { var fullSize = loadImage2(file); ret scaleBufferedImageToMaxWidthOrHeight(thumbnailSize, fullSize); } selfType thumbnailSize(int wAndH) { ret thumbnailSize(new WidthAndHeightImpl(wAndH)); } void get(File imageFile, IVF1 onLoad) { lock lock; if (map.containsKey(imageFile)) onLoad.get(map.get(imageFile)); else q.add(-> { BufferedImage img = null; pcall { img = makeThumbnail(imageFile); } { lock lock; usedBytes += guessImageBytes(img); map.put(imageFile, img); } onLoad.get(img); }); } }