Added #1875 Remove no longer accessed items from cache after certain amount of time

This commit is contained in:
Jindra Petřík
2022-11-17 08:49:30 +01:00
parent 77a6b6a019
commit 202e66ea1d
12 changed files with 103 additions and 19 deletions

View File

@@ -47,9 +47,9 @@ public class IdentifiersDeobfuscation {
private final HashMap<String, Integer> typeCounts = new HashMap<>();
private static final Cache<String, String> as2NameCache = Cache.getInstance(false, true, "as2_ident");
private static final Cache<String, String> as2NameCache = Cache.getInstance(false, true, "as2_ident", true);
private static final Cache<String, String> as3NameCache = Cache.getInstance(false, true, "as3_ident");
private static final Cache<String, String> as3NameCache = Cache.getInstance(false, true, "as3_ident", true);
public static final String VALID_FIRST_CHARACTERS = "\\p{Lu}\\p{Ll}\\p{Lt}\\p{Lm}\\p{Lo}_$";

View File

@@ -341,16 +341,16 @@ public final class SWF implements SWFContainerItem, Timelined {
private final IdentifiersDeobfuscation deobfuscation = new IdentifiersDeobfuscation();
@Internal
private final Cache<String, SerializableImage> frameCache = Cache.getInstance(false, false, "frame");
private final Cache<String, SerializableImage> frameCache = Cache.getInstance(false, false, "frame", true);
@Internal
private final Cache<CharacterTag, RECT> rectCache = Cache.getInstance(true, true, "rect");
private final Cache<CharacterTag, RECT> rectCache = Cache.getInstance(true, true, "rect", true);
@Internal
private final Cache<SHAPE, ShapeExportData> shapeExportDataCache = Cache.getInstance(true, true, "shapeExportData");
private final Cache<SHAPE, ShapeExportData> shapeExportDataCache = Cache.getInstance(true, true, "shapeExportData", true);
@Internal
private final Cache<SoundInfoSoundCacheEntry, byte[]> soundCache = Cache.getInstance(false, false, "sound");
private final Cache<SoundInfoSoundCacheEntry, byte[]> soundCache = Cache.getInstance(false, false, "sound", true);
@Internal
public final AS2Cache as2Cache = new AS2Cache();

View File

@@ -27,9 +27,9 @@ import com.jpexs.helpers.Cache;
*/
public class AS2Cache {
private final Cache<ASMSource, HighlightedText> cache = Cache.getInstance(true, false, "as2");
private final Cache<ASMSource, HighlightedText> cache = Cache.getInstance(true, false, "as2", false);
private final Cache<ASMSource, ActionList> pcodeCache = Cache.getInstance(true, true, "as2pcode");
private final Cache<ASMSource, ActionList> pcodeCache = Cache.getInstance(true, true, "as2pcode", false);
public void clear() {
pcodeCache.clear();

View File

@@ -26,7 +26,7 @@ import com.jpexs.helpers.Cache;
*/
public class AS3Cache {
private final Cache<ScriptPack, HighlightedText> cache = Cache.getInstance(true, false, "as3");
private final Cache<ScriptPack, HighlightedText> cache = Cache.getInstance(true, false, "as3", false);
public void clear() {
cache.clear();

View File

@@ -787,6 +787,10 @@ public final class Configuration {
@ConfigurationDefaultBoolean(true)
@ConfigurationCategory("display")
public static ConfigurationItem<Boolean> autoPlayPreviews = null;
@ConfigurationDefaultInt(5 * 60 * 1000)
@ConfigurationCategory("limit")
public static ConfigurationItem<Integer> maxCachedTime = null;
private enum OSId {
WINDOWS, OSX, UNIX

View File

@@ -31,7 +31,7 @@ import java.util.logging.Logger;
public class AbstractDocs {
protected static Cache<String, String> docsCache = Cache.getInstance(false, true, "abstractDocsCache");
protected static Cache<String, String> docsCache = Cache.getInstance(false, true, "abstractDocsCache", false);
protected static String htmlFooter() {
StringBuilder sb = new StringBuilder();

View File

@@ -16,6 +16,7 @@
*/
package com.jpexs.helpers;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.helpers.Freed;
import java.io.File;
import java.io.IOException;
@@ -27,6 +28,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
@@ -37,6 +40,7 @@ import java.util.WeakHashMap;
public class Cache<K, V> implements Freed {
private Map<K, V> cache;
private Map<K, Long> lastAccessed;
private static final List<WeakReference<Cache>> instances = new ArrayList<>();
@@ -49,6 +53,12 @@ public class Cache<K, V> implements Freed {
private final boolean memoryOnly;
private final String name;
private final boolean temporary;
private static final long CLEAN_INTERVAL = 5 * 1000; //5 seconds
private static Thread oldCleaner = null;
static {
Runtime.getRuntime().addShutdownHook(new Thread() {
@@ -67,8 +77,24 @@ public class Cache<K, V> implements Freed {
});
}
public static <K, V> Cache<K, V> getInstance(boolean weak, boolean memoryOnly, String name) {
Cache<K, V> instance = new Cache<>(weak, memoryOnly, name);
public static <K, V> Cache<K, V> getInstance(boolean weak, boolean memoryOnly, String name, boolean temporary) {
if (oldCleaner == null) {
oldCleaner = new Thread("Old cache cleaner") {
@Override
public void run() {
while(!Thread.interrupted()) {
try {
Thread.sleep(CLEAN_INTERVAL);
} catch (InterruptedException ex) {
return;
}
clearAllOld();
}
}
};
oldCleaner.start();
}
Cache<K, V> instance = new Cache<>(weak, memoryOnly, name, temporary);
instances.add(new WeakReference<>(instance));
return instance;
}
@@ -129,36 +155,48 @@ public class Cache<K, V> implements Freed {
if (this.cache instanceof Freed) {
((Freed) this.cache).free();
}
this.lastAccessed = new WeakHashMap<>();
this.cache = newCache;
}
private Cache(boolean weak, boolean memoryOnly, String name) {
private Cache(boolean weak, boolean memoryOnly, String name, boolean temporary) {
this.weak = weak;
this.name = name;
this.memoryOnly = memoryOnly;
this.temporary = temporary;
initCache();
}
public synchronized boolean contains(K key) {
return cache.containsKey(key);
public synchronized boolean contains(K key) {
boolean ret = cache.containsKey(key);
if (ret) {
lastAccessed.put(key, System.currentTimeMillis());
}
return ret;
}
public synchronized void clear() {
cache.clear();
lastAccessed.clear();
}
public synchronized void remove(K key) {
if (cache.containsKey(key)) {
cache.remove(key);
}
if (lastAccessed.containsKey(key)) {
lastAccessed.remove(key);
}
}
public synchronized V get(K key) {
lastAccessed.put(key, System.currentTimeMillis());
return cache.get(key);
}
public synchronized void put(K key, V value) {
cache.put(key, value);
lastAccessed.put(key, System.currentTimeMillis());
}
@Override
@@ -178,4 +216,37 @@ public class Cache<K, V> implements Freed {
ret.addAll(cache.keySet());
return ret;
}
private synchronized int clearOld() {
long currentTime = System.currentTimeMillis();
Set<K> keys = new HashSet<>(lastAccessed.keySet());
int temporaryThreshold = Configuration.maxCachedTime.get();
if (temporaryThreshold == 0) {
return 0;
}
int num = 0;
for(K key:keys) {
long time = lastAccessed.get(key);
if (time < currentTime - temporaryThreshold) {
remove(key);
num++;
}
}
return num;
}
private static void clearAllOld() {
int num = 0;
for (WeakReference<Cache> cw : instances) {
Cache c = cw.get();
if (c != null) {
if (c.temporary) {
num += c.clearOld();
}
}
}
if (num > 0) {
System.gc();
}
}
}