From 267c3c55df9996c0fbe21a7ce891b3f8d978816d Mon Sep 17 00:00:00 2001 From: Alexander Kochurov Date: Thu, 13 Aug 2015 16:27:17 +0400 Subject: [PATCH] Reduce memory consumption of CleanableRegister Each class dependency node used to store a reference to each of its child class caches. Now instead of adding caches to every parent class list a dependency is added. --- .../com/maxifier/mxcache/CacheFactory.java | 4 ++ .../mxcache/clean/CleanableRegister.java | 64 +++++++++++++++---- .../mxcache/impl/AbstractCacheManager.java | 19 +----- 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/mxcache-runtime/src/main/java/com/maxifier/mxcache/CacheFactory.java b/mxcache-runtime/src/main/java/com/maxifier/mxcache/CacheFactory.java index dacaaa00..96bd5bb1 100644 --- a/mxcache-runtime/src/main/java/com/maxifier/mxcache/CacheFactory.java +++ b/mxcache-runtime/src/main/java/com/maxifier/mxcache/CacheFactory.java @@ -63,6 +63,10 @@ public static DependencyNode getClassDependencyNode(Class clazz) { return REGISTRY.getClassDependencyNode(clazz); } + public static DependencyNode getClassInstanceDependencyNode(Class clazz) { + return REGISTRY.getClassInstanceDependencyNode(clazz); + } + public static DependencyNode getTagDependencyNode(String tag) { return REGISTRY.getTagDependencyNode(tag); } diff --git a/mxcache-runtime/src/main/java/com/maxifier/mxcache/clean/CleanableRegister.java b/mxcache-runtime/src/main/java/com/maxifier/mxcache/clean/CleanableRegister.java index d1d1685d..7de1050d 100644 --- a/mxcache-runtime/src/main/java/com/maxifier/mxcache/clean/CleanableRegister.java +++ b/mxcache-runtime/src/main/java/com/maxifier/mxcache/clean/CleanableRegister.java @@ -32,9 +32,20 @@ public final class CleanableRegister implements CacheCleaner { private static final Logger logger = LoggerFactory.getLogger(CleanableRegister.class); private final Map, ClassCleanableInstanceList> classCleanMap = new THashMap, ClassCleanableInstanceList>(); - private final Map, DependencyNode> classMapping = new THashMap, DependencyNode>(); - private final Map groupMapping = new THashMap(); - private final Map tagMapping = new THashMap(); + + /** + * For each class there are two DependencyNodes: one for static methods and one for its instance methods. + * + * Instance method node depends on static node and on instance nodes of parent classes and interfaces. + * + * So it's enough to invalidate static node to invalidate all static caches of cache, instance caches of this + * class and all instance caches of its child classes too. This is what {@link #clearCacheByClass(Class)} does. + */ + private final Map, DependencyNode> classStaticNodes = new THashMap, DependencyNode>(); + private final Map, DependencyNode> classInstanceNodes = new THashMap, DependencyNode>(); + + private final Map groupNodes = new THashMap(); + private final Map tagNodes = new THashMap(); private static final Cleanable EMPTY_CLEANABLE = new EmptyCleanable(); @@ -191,7 +202,7 @@ public void clearCacheByClass(Class clazz) { DependencyNode node; synchronized (this) { - node = classMapping.get(clazz); + node = classStaticNodes.get(clazz); } if (node == null) { logger.warn("There is no subclasses of " + clazz + " with caches yet"); @@ -204,7 +215,7 @@ public void clearCacheByClass(Class clazz) { public void clearCacheByGroup(String group) { DependencyNode node; synchronized (this) { - node = groupMapping.get(group); + node = groupNodes.get(group); } if (node == null) { logger.warn("There is no caches of group <" + group + "> yet"); @@ -217,7 +228,7 @@ public void clearCacheByGroup(String group) { public void clearCacheByTag(String tag) { DependencyNode node; synchronized (this) { - node = tagMapping.get(tag); + node = tagNodes.get(tag); } if (node == null) { logger.warn("There is no caches with tag <" + tag + "> yet"); @@ -233,7 +244,7 @@ public void clearCacheByTag(String tag) { public void clearCacheByAnnotation(Class annotationClass) { DependencyNode node; synchronized (this) { - node = tagMapping.get("@" + annotationClass.getName()); + node = tagNodes.get("@" + annotationClass.getName()); } if (node == null) { logger.warn("Can`t find cache for annotation " + annotationClass); @@ -242,30 +253,55 @@ public void clearCacheByAnnotation(Class annotationClass) } } - @Nullable + @Nonnull public synchronized DependencyNode getClassDependencyNode(Class clazz) { - DependencyNode tagNode = classMapping.get(clazz); + DependencyNode tagNode = classStaticNodes.get(clazz); if (tagNode == null) { tagNode = new EmptyDependencyNode("class:" + clazz.getName()); - classMapping.put(clazz, tagNode); + classStaticNodes.put(clazz, tagNode); + } + return tagNode; + } + + @Nonnull + public synchronized DependencyNode getClassInstanceDependencyNode(Class clazz) { + DependencyNode tagNode = classInstanceNodes.get(clazz); + if (tagNode == null) { + tagNode = new EmptyDependencyNode("instance:" + clazz.getName()); + trackParentAndInterfaceDependencies(clazz, tagNode); + + classInstanceNodes.put(clazz, tagNode); } return tagNode; } + private void trackParentAndInterfaceDependencies(Class clazz, DependencyNode tagNode) { + Class superclass = clazz.getSuperclass(); + if (superclass != null) { + getClassInstanceDependencyNode(superclass).trackDependency(tagNode); + } + for (Class intf : clazz.getInterfaces()) { + getClassInstanceDependencyNode(intf).trackDependency(tagNode); + } + getClassDependencyNode(clazz).trackDependency(tagNode); + } + + @Nonnull public synchronized DependencyNode getTagDependencyNode(String tag) { - DependencyNode tagNode = tagMapping.get(tag); + DependencyNode tagNode = tagNodes.get(tag); if (tagNode == null) { tagNode = new EmptyDependencyNode("tag:" + tag); - tagMapping.put(tag, tagNode); + tagNodes.put(tag, tagNode); } return tagNode; } + @Nonnull public synchronized DependencyNode getGroupDependencyNode(String group) { - DependencyNode groupNode = groupMapping.get(group); + DependencyNode groupNode = groupNodes.get(group); if (groupNode == null) { groupNode = new EmptyDependencyNode("group:" + group); - groupMapping.put(group, groupNode); + groupNodes.put(group, groupNode); } return groupNode; } diff --git a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/AbstractCacheManager.java b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/AbstractCacheManager.java index 3ee9944e..28c15122 100644 --- a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/AbstractCacheManager.java +++ b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/AbstractCacheManager.java @@ -12,7 +12,6 @@ import com.maxifier.mxcache.impl.resource.DependencyNode; import com.maxifier.mxcache.impl.resource.DependencyTracker; import com.maxifier.mxcache.caches.Cache; -import gnu.trove.set.hash.THashSet; import javax.annotation.Nullable; @@ -104,27 +103,15 @@ private static DependencyNode[] getExplicitDependencies(CacheDescriptor descript res.add(CacheFactory.getGroupDependencyNode(group)); } if (descriptor.isStatic()) { - // static cache is not invalidated when a superclass of declaring class is invalidated + // static cache is not invalidated when a superclass of declaring class is invalidated, + // so we put it to a separate node res.add(CacheFactory.getClassDependencyNode(ownerClass)); } else { - Set> visitedClasses = new THashSet>(); - addAllSuperClassesAndInterfaces(res, visitedClasses, ownerClass); + res.add(CacheFactory.getClassInstanceDependencyNode(ownerClass)); } return res.isEmpty() ? null : res.toArray(new DependencyNode[res.size()]); } - private static void addAllSuperClassesAndInterfaces(List res, Set> visitedClasses, Class clazz) { - while (clazz != null && clazz != Object.class) { - if (visitedClasses.add(clazz)) { - res.add(CacheFactory.getClassDependencyNode(clazz)); - for (Class intf : clazz.getInterfaces()) { - addAllSuperClassesAndInterfaces(res, visitedClasses, intf); - } - } - clazz = clazz.getSuperclass(); - } - } - protected StatisticsModeEnum getStatisticsMode() { return statisticsMode; }