diff --git a/CHANGELOG.md b/CHANGELOG.md index 67de076d0..97cc14b66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ### Added - [#1870] AS3 Adding new class - Target DoABC tag or position can be selected to prevent Error 1014 - [#1871] Toogle buttons for disabling subsprite animation, display preview of sprites/frames +- [#1875] Remove no longer accessed items from cache after certain amount of time ### Fixed - [#1869] Replace references now replaces all references, not just PlaceObject @@ -2588,6 +2589,7 @@ All notable changes to this project will be documented in this file. [alpha 7]: https://github.com/jindrapetrik/jpexs-decompiler/releases/tag/alpha7 [#1870]: https://www.free-decompiler.com/flash/issues/1870 [#1871]: https://www.free-decompiler.com/flash/issues/1871 +[#1875]: https://www.free-decompiler.com/flash/issues/1875 [#1869]: https://www.free-decompiler.com/flash/issues/1869 [#1872]: https://www.free-decompiler.com/flash/issues/1872 [#1867]: https://www.free-decompiler.com/flash/issues/1867 diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java index 5073a2759..c5d6a20f3 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/IdentifiersDeobfuscation.java @@ -47,9 +47,9 @@ public class IdentifiersDeobfuscation { private final HashMap typeCounts = new HashMap<>(); - private static final Cache as2NameCache = Cache.getInstance(false, true, "as2_ident"); + private static final Cache as2NameCache = Cache.getInstance(false, true, "as2_ident", true); - private static final Cache as3NameCache = Cache.getInstance(false, true, "as3_ident"); + private static final Cache 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}_$"; diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java index aa6677243..0421eff81 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/SWF.java @@ -341,16 +341,16 @@ public final class SWF implements SWFContainerItem, Timelined { private final IdentifiersDeobfuscation deobfuscation = new IdentifiersDeobfuscation(); @Internal - private final Cache frameCache = Cache.getInstance(false, false, "frame"); + private final Cache frameCache = Cache.getInstance(false, false, "frame", true); @Internal - private final Cache rectCache = Cache.getInstance(true, true, "rect"); + private final Cache rectCache = Cache.getInstance(true, true, "rect", true); @Internal - private final Cache shapeExportDataCache = Cache.getInstance(true, true, "shapeExportData"); + private final Cache shapeExportDataCache = Cache.getInstance(true, true, "shapeExportData", true); @Internal - private final Cache soundCache = Cache.getInstance(false, false, "sound"); + private final Cache soundCache = Cache.getInstance(false, false, "sound", true); @Internal public final AS2Cache as2Cache = new AS2Cache(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/cache/AS2Cache.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/cache/AS2Cache.java index 8867b8c6b..4723cb25b 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/cache/AS2Cache.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/cache/AS2Cache.java @@ -27,9 +27,9 @@ import com.jpexs.helpers.Cache; */ public class AS2Cache { - private final Cache cache = Cache.getInstance(true, false, "as2"); + private final Cache cache = Cache.getInstance(true, false, "as2", false); - private final Cache pcodeCache = Cache.getInstance(true, true, "as2pcode"); + private final Cache pcodeCache = Cache.getInstance(true, true, "as2pcode", false); public void clear() { pcodeCache.clear(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/cache/AS3Cache.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/cache/AS3Cache.java index 5034d1a3c..6f0c94a4d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/cache/AS3Cache.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/cache/AS3Cache.java @@ -26,7 +26,7 @@ import com.jpexs.helpers.Cache; */ public class AS3Cache { - private final Cache cache = Cache.getInstance(true, false, "as3"); + private final Cache cache = Cache.getInstance(true, false, "as3", false); public void clear() { cache.clear(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index d397af7ec..da85fb31d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -787,6 +787,10 @@ public final class Configuration { @ConfigurationDefaultBoolean(true) @ConfigurationCategory("display") public static ConfigurationItem autoPlayPreviews = null; + + @ConfigurationDefaultInt(5 * 60 * 1000) + @ConfigurationCategory("limit") + public static ConfigurationItem maxCachedTime = null; private enum OSId { WINDOWS, OSX, UNIX diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/docs/AbstractDocs.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/docs/AbstractDocs.java index 8f86f265c..1abe94e0d 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/docs/AbstractDocs.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/docs/AbstractDocs.java @@ -31,7 +31,7 @@ import java.util.logging.Logger; public class AbstractDocs { - protected static Cache docsCache = Cache.getInstance(false, true, "abstractDocsCache"); + protected static Cache docsCache = Cache.getInstance(false, true, "abstractDocsCache", false); protected static String htmlFooter() { StringBuilder sb = new StringBuilder(); diff --git a/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java b/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java index 7a39fd901..66aa7b6ff 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java +++ b/libsrc/ffdec_lib/src/com/jpexs/helpers/Cache.java @@ -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 implements Freed { private Map cache; + private Map lastAccessed; private static final List> instances = new ArrayList<>(); @@ -49,6 +53,12 @@ public class Cache 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 implements Freed { }); } - public static Cache getInstance(boolean weak, boolean memoryOnly, String name) { - Cache instance = new Cache<>(weak, memoryOnly, name); + public static Cache 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 instance = new Cache<>(weak, memoryOnly, name, temporary); instances.add(new WeakReference<>(instance)); return instance; } @@ -129,36 +155,48 @@ public class Cache 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 implements Freed { ret.addAll(cache.keySet()); return ret; } + + private synchronized int clearOld() { + long currentTime = System.currentTimeMillis(); + Set 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 cw : instances) { + Cache c = cw.get(); + if (c != null) { + if (c.temporary) { + num += c.clearOld(); + } + } + } + if (num > 0) { + System.gc(); + } + } } diff --git a/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java b/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java index b350e69d4..64ba75647 100644 --- a/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java +++ b/src/com/jpexs/decompiler/flash/gui/FolderPreviewPanel.java @@ -99,7 +99,7 @@ public class FolderPreviewPanel extends JPanel { public FolderPreviewPanel(final MainPanel mainPanel, List items) { this.items = items; - cachedPreviews = Cache.getInstance(false, false, "preview"); + cachedPreviews = Cache.getInstance(false, false, "preview", true); addMouseListener(new MouseAdapter() { @Override diff --git a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java index 0fb254e21..12ab2b5af 100644 --- a/src/com/jpexs/decompiler/flash/gui/ImagePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/ImagePanel.java @@ -150,7 +150,7 @@ public final class ImagePanel extends JPanel implements MediaDisplay { private final List soundPlayers = new ArrayList<>(); - private final Cache displayObjectCache = Cache.getInstance(false, false, "displayObject"); + private final Cache displayObjectCache = Cache.getInstance(false, false, "displayObject", true); private final IconPanel iconPanel; diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties index 66144b72d..725c16d75 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog.properties @@ -601,4 +601,8 @@ config.name.animateSubsprites = Animate subsprites in preview config.description.allowMiterClipLinestyle = Allow subsprite animation on timeline preview. config.name.autoPlayPreviews = Autoplay previews -config.description.autoPlayPreviews = Automatically play previews. \ No newline at end of file +config.description.autoPlayPreviews = Automatically play previews. + +config.name.maxCachedTime = Maximum temporary cache time +config.description.maxCachedTime = Maximum time in milliseconds before item (which was not accessed since) is removed from cache. Set this to 0 to unlimited caching. + diff --git a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties index 7e827c026..fd3f1a697 100644 --- a/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties +++ b/src/com/jpexs/decompiler/flash/gui/locales/AdvancedSettingsDialog_cs.properties @@ -588,4 +588,7 @@ config.name.animateSubsprites = Animovat podsprity v n\u00e1hledu config.description.allowMiterClipLinestyle = Povolit animace podsprit\u016f v n\u00e1hledu timeliny. config.name.autoPlayPreviews = Automaticky p\u0159ehr\u00e1vat n\u00e1hledy -config.description.autoPlayPreviews = Automaticky p\u0159ehr\u00e1vat n\u00e1hledy. \ No newline at end of file +config.description.autoPlayPreviews = Automaticky p\u0159ehr\u00e1vat n\u00e1hledy. + +config.name.maxCachedTime = Maxim\u00e1ln\u00ed \u010das do\u010dasn\u00e9 cache +config.description.maxCachedTime = Maxim\u00e1ln\u00ed \u010das v milisekund\u00e1ch, kter\u00fd mus\u00ed ub\u011bhnout, aby byla polo\u017eka(kter\u00e1 nebyla mezit\u00edm aktivn\u00ed) odstran\u011bna z cache. Nastavte sem hodnotu 0 pro nekone\u010dn\u00e9 cachov\u00e1n\u00ed.