diff --git a/mxcache-generation/src/test/java/com/maxifier/mxcache/template/impl/PInlineDependencyCache.template b/mxcache-generation/src/test/java/com/maxifier/mxcache/template/impl/PInlineDependencyCache.template index 1361972e..7463121b 100644 --- a/mxcache-generation/src/test/java/com/maxifier/mxcache/template/impl/PInlineDependencyCache.template +++ b/mxcache-generation/src/test/java/com/maxifier/mxcache/template/impl/PInlineDependencyCache.template @@ -32,6 +32,14 @@ public class #E#InlineDependencyCache extends #E#InlineCacheImpl implements Depe */ private Set> dependentNodes; + /** + * Number of elements in dependentNodes after which all the set should be checked for the presence of + * references to GC'ed objects. + * + * This threshold is required in order to evict such references as they pollute memory and never GC'ed otherwise. + */ + private int cleanupThreshold = 10; + private Reference selfReference; public #E#InlineDependencyCache(Object owner, #E#Calculatable calculable, MutableStatistics statistics) { @@ -68,6 +76,26 @@ public class #E#InlineDependencyCache extends #E#InlineCacheImpl implements Depe dependentNodes = new THashSet>(); } dependentNodes.add(node.getSelfReference()); + cleanupIfNeeded(); + } + + private void cleanupIfNeeded() { + if (dependentNodes.size() >= cleanupThreshold) { + for (Iterator> it = dependentNodes.iterator(); it.hasNext(); ) { + if (it.next().get() == null) { + it.remove(); + } + } + // It's important to increase cleanup threshold according to the number of elements in a set + // in order to maintain the balance between CPU-overhead and memory-overhead + + // The cleanup has O(N) complexity, so doing this on addition of N new elements would lead to constant + // small overhead and thus would not affect the asymptotic behaviour of operations. + + // The memory overhead could be significant but it's guaranteed that memory usage would not be more than + // 2 * peak memory usage for alive elements. + cleanupThreshold = dependentNodes.size() * 2; + } } @Override diff --git a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/BooleanInlineDependencyCache.java b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/BooleanInlineDependencyCache.java index 6b6eecfe..c6aefa57 100644 --- a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/BooleanInlineDependencyCache.java +++ b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/BooleanInlineDependencyCache.java @@ -32,6 +32,14 @@ public class BooleanInlineDependencyCache extends BooleanInlineCacheImpl impleme */ private Set> dependentNodes; + /** + * Number of elements in dependentNodes after which all the set should be checked for the presence of + * references to GC'ed objects. + * + * This threshold is required in order to evict such references as they pollute memory and never GC'ed otherwise. + */ + private int cleanupThreshold = 10; + private Reference selfReference; public BooleanInlineDependencyCache(Object owner, BooleanCalculatable calculable, MutableStatistics statistics) { @@ -68,6 +76,26 @@ public synchronized void trackDependency(DependencyNode node) { dependentNodes = new THashSet>(); } dependentNodes.add(node.getSelfReference()); + cleanupIfNeeded(); + } + + private void cleanupIfNeeded() { + if (dependentNodes.size() >= cleanupThreshold) { + for (Iterator> it = dependentNodes.iterator(); it.hasNext(); ) { + if (it.next().get() == null) { + it.remove(); + } + } + // It's important to increase cleanup threshold according to the number of elements in a set + // in order to maintain the balance between CPU-overhead and memory-overhead + + // The cleanup has O(N) complexity, so doing this on addition of N new elements would lead to constant + // small overhead and thus would not affect the asymptotic behaviour of operations. + + // The memory overhead could be significant but it's guaranteed that memory usage would not be more than + // 2 * peak memory usage for alive elements. + cleanupThreshold = dependentNodes.size() * 2; + } } @Override diff --git a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/ByteInlineDependencyCache.java b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/ByteInlineDependencyCache.java index 38d88b3e..a202bf0f 100644 --- a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/ByteInlineDependencyCache.java +++ b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/ByteInlineDependencyCache.java @@ -32,6 +32,14 @@ public class ByteInlineDependencyCache extends ByteInlineCacheImpl implements De */ private Set> dependentNodes; + /** + * Number of elements in dependentNodes after which all the set should be checked for the presence of + * references to GC'ed objects. + * + * This threshold is required in order to evict such references as they pollute memory and never GC'ed otherwise. + */ + private int cleanupThreshold = 10; + private Reference selfReference; public ByteInlineDependencyCache(Object owner, ByteCalculatable calculable, MutableStatistics statistics) { @@ -68,6 +76,26 @@ public synchronized void trackDependency(DependencyNode node) { dependentNodes = new THashSet>(); } dependentNodes.add(node.getSelfReference()); + cleanupIfNeeded(); + } + + private void cleanupIfNeeded() { + if (dependentNodes.size() >= cleanupThreshold) { + for (Iterator> it = dependentNodes.iterator(); it.hasNext(); ) { + if (it.next().get() == null) { + it.remove(); + } + } + // It's important to increase cleanup threshold according to the number of elements in a set + // in order to maintain the balance between CPU-overhead and memory-overhead + + // The cleanup has O(N) complexity, so doing this on addition of N new elements would lead to constant + // small overhead and thus would not affect the asymptotic behaviour of operations. + + // The memory overhead could be significant but it's guaranteed that memory usage would not be more than + // 2 * peak memory usage for alive elements. + cleanupThreshold = dependentNodes.size() * 2; + } } @Override diff --git a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/CharacterInlineDependencyCache.java b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/CharacterInlineDependencyCache.java index ff899bfa..8d3da4a6 100644 --- a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/CharacterInlineDependencyCache.java +++ b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/CharacterInlineDependencyCache.java @@ -32,6 +32,14 @@ public class CharacterInlineDependencyCache extends CharacterInlineCacheImpl imp */ private Set> dependentNodes; + /** + * Number of elements in dependentNodes after which all the set should be checked for the presence of + * references to GC'ed objects. + * + * This threshold is required in order to evict such references as they pollute memory and never GC'ed otherwise. + */ + private int cleanupThreshold = 10; + private Reference selfReference; public CharacterInlineDependencyCache(Object owner, CharacterCalculatable calculable, MutableStatistics statistics) { @@ -68,6 +76,26 @@ public synchronized void trackDependency(DependencyNode node) { dependentNodes = new THashSet>(); } dependentNodes.add(node.getSelfReference()); + cleanupIfNeeded(); + } + + private void cleanupIfNeeded() { + if (dependentNodes.size() >= cleanupThreshold) { + for (Iterator> it = dependentNodes.iterator(); it.hasNext(); ) { + if (it.next().get() == null) { + it.remove(); + } + } + // It's important to increase cleanup threshold according to the number of elements in a set + // in order to maintain the balance between CPU-overhead and memory-overhead + + // The cleanup has O(N) complexity, so doing this on addition of N new elements would lead to constant + // small overhead and thus would not affect the asymptotic behaviour of operations. + + // The memory overhead could be significant but it's guaranteed that memory usage would not be more than + // 2 * peak memory usage for alive elements. + cleanupThreshold = dependentNodes.size() * 2; + } } @Override diff --git a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/DoubleInlineDependencyCache.java b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/DoubleInlineDependencyCache.java index a8d261ca..0eef3265 100644 --- a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/DoubleInlineDependencyCache.java +++ b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/DoubleInlineDependencyCache.java @@ -32,6 +32,14 @@ public class DoubleInlineDependencyCache extends DoubleInlineCacheImpl implement */ private Set> dependentNodes; + /** + * Number of elements in dependentNodes after which all the set should be checked for the presence of + * references to GC'ed objects. + * + * This threshold is required in order to evict such references as they pollute memory and never GC'ed otherwise. + */ + private int cleanupThreshold = 10; + private Reference selfReference; public DoubleInlineDependencyCache(Object owner, DoubleCalculatable calculable, MutableStatistics statistics) { @@ -68,6 +76,26 @@ public synchronized void trackDependency(DependencyNode node) { dependentNodes = new THashSet>(); } dependentNodes.add(node.getSelfReference()); + cleanupIfNeeded(); + } + + private void cleanupIfNeeded() { + if (dependentNodes.size() >= cleanupThreshold) { + for (Iterator> it = dependentNodes.iterator(); it.hasNext(); ) { + if (it.next().get() == null) { + it.remove(); + } + } + // It's important to increase cleanup threshold according to the number of elements in a set + // in order to maintain the balance between CPU-overhead and memory-overhead + + // The cleanup has O(N) complexity, so doing this on addition of N new elements would lead to constant + // small overhead and thus would not affect the asymptotic behaviour of operations. + + // The memory overhead could be significant but it's guaranteed that memory usage would not be more than + // 2 * peak memory usage for alive elements. + cleanupThreshold = dependentNodes.size() * 2; + } } @Override diff --git a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/FloatInlineDependencyCache.java b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/FloatInlineDependencyCache.java index 682e812c..47982973 100644 --- a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/FloatInlineDependencyCache.java +++ b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/FloatInlineDependencyCache.java @@ -32,6 +32,14 @@ public class FloatInlineDependencyCache extends FloatInlineCacheImpl implements */ private Set> dependentNodes; + /** + * Number of elements in dependentNodes after which all the set should be checked for the presence of + * references to GC'ed objects. + * + * This threshold is required in order to evict such references as they pollute memory and never GC'ed otherwise. + */ + private int cleanupThreshold = 10; + private Reference selfReference; public FloatInlineDependencyCache(Object owner, FloatCalculatable calculable, MutableStatistics statistics) { @@ -68,6 +76,26 @@ public synchronized void trackDependency(DependencyNode node) { dependentNodes = new THashSet>(); } dependentNodes.add(node.getSelfReference()); + cleanupIfNeeded(); + } + + private void cleanupIfNeeded() { + if (dependentNodes.size() >= cleanupThreshold) { + for (Iterator> it = dependentNodes.iterator(); it.hasNext(); ) { + if (it.next().get() == null) { + it.remove(); + } + } + // It's important to increase cleanup threshold according to the number of elements in a set + // in order to maintain the balance between CPU-overhead and memory-overhead + + // The cleanup has O(N) complexity, so doing this on addition of N new elements would lead to constant + // small overhead and thus would not affect the asymptotic behaviour of operations. + + // The memory overhead could be significant but it's guaranteed that memory usage would not be more than + // 2 * peak memory usage for alive elements. + cleanupThreshold = dependentNodes.size() * 2; + } } @Override diff --git a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/IntInlineDependencyCache.java b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/IntInlineDependencyCache.java index e5511c0e..6faca6f5 100644 --- a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/IntInlineDependencyCache.java +++ b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/IntInlineDependencyCache.java @@ -32,6 +32,14 @@ public class IntInlineDependencyCache extends IntInlineCacheImpl implements Depe */ private Set> dependentNodes; + /** + * Number of elements in dependentNodes after which all the set should be checked for the presence of + * references to GC'ed objects. + * + * This threshold is required in order to evict such references as they pollute memory and never GC'ed otherwise. + */ + private int cleanupThreshold = 10; + private Reference selfReference; public IntInlineDependencyCache(Object owner, IntCalculatable calculable, MutableStatistics statistics) { @@ -68,6 +76,26 @@ public synchronized void trackDependency(DependencyNode node) { dependentNodes = new THashSet>(); } dependentNodes.add(node.getSelfReference()); + cleanupIfNeeded(); + } + + private void cleanupIfNeeded() { + if (dependentNodes.size() >= cleanupThreshold) { + for (Iterator> it = dependentNodes.iterator(); it.hasNext(); ) { + if (it.next().get() == null) { + it.remove(); + } + } + // It's important to increase cleanup threshold according to the number of elements in a set + // in order to maintain the balance between CPU-overhead and memory-overhead + + // The cleanup has O(N) complexity, so doing this on addition of N new elements would lead to constant + // small overhead and thus would not affect the asymptotic behaviour of operations. + + // The memory overhead could be significant but it's guaranteed that memory usage would not be more than + // 2 * peak memory usage for alive elements. + cleanupThreshold = dependentNodes.size() * 2; + } } @Override diff --git a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/LongInlineDependencyCache.java b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/LongInlineDependencyCache.java index 1a1a5bf8..0a2e643e 100644 --- a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/LongInlineDependencyCache.java +++ b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/LongInlineDependencyCache.java @@ -32,6 +32,14 @@ public class LongInlineDependencyCache extends LongInlineCacheImpl implements De */ private Set> dependentNodes; + /** + * Number of elements in dependentNodes after which all the set should be checked for the presence of + * references to GC'ed objects. + * + * This threshold is required in order to evict such references as they pollute memory and never GC'ed otherwise. + */ + private int cleanupThreshold = 10; + private Reference selfReference; public LongInlineDependencyCache(Object owner, LongCalculatable calculable, MutableStatistics statistics) { @@ -68,6 +76,26 @@ public synchronized void trackDependency(DependencyNode node) { dependentNodes = new THashSet>(); } dependentNodes.add(node.getSelfReference()); + cleanupIfNeeded(); + } + + private void cleanupIfNeeded() { + if (dependentNodes.size() >= cleanupThreshold) { + for (Iterator> it = dependentNodes.iterator(); it.hasNext(); ) { + if (it.next().get() == null) { + it.remove(); + } + } + // It's important to increase cleanup threshold according to the number of elements in a set + // in order to maintain the balance between CPU-overhead and memory-overhead + + // The cleanup has O(N) complexity, so doing this on addition of N new elements would lead to constant + // small overhead and thus would not affect the asymptotic behaviour of operations. + + // The memory overhead could be significant but it's guaranteed that memory usage would not be more than + // 2 * peak memory usage for alive elements. + cleanupThreshold = dependentNodes.size() * 2; + } } @Override diff --git a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/ObjectInlineDependencyCache.java b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/ObjectInlineDependencyCache.java index 3bc92e96..da587d67 100644 --- a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/ObjectInlineDependencyCache.java +++ b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/ObjectInlineDependencyCache.java @@ -32,6 +32,14 @@ public class ObjectInlineDependencyCache extends ObjectInlineCacheImpl implement */ private Set> dependentNodes; + /** + * Number of elements in dependentNodes after which all the set should be checked for the presence of + * references to GC'ed objects. + * + * This threshold is required in order to evict such references as they pollute memory and never GC'ed otherwise. + */ + private int cleanupThreshold = 10; + private Reference selfReference; public ObjectInlineDependencyCache(Object owner, ObjectCalculatable calculable, MutableStatistics statistics) { @@ -68,6 +76,26 @@ public synchronized void trackDependency(DependencyNode node) { dependentNodes = new THashSet>(); } dependentNodes.add(node.getSelfReference()); + cleanupIfNeeded(); + } + + private void cleanupIfNeeded() { + if (dependentNodes.size() >= cleanupThreshold) { + for (Iterator> it = dependentNodes.iterator(); it.hasNext(); ) { + if (it.next().get() == null) { + it.remove(); + } + } + // It's important to increase cleanup threshold according to the number of elements in a set + // in order to maintain the balance between CPU-overhead and memory-overhead + + // The cleanup has O(N) complexity, so doing this on addition of N new elements would lead to constant + // small overhead and thus would not affect the asymptotic behaviour of operations. + + // The memory overhead could be significant but it's guaranteed that memory usage would not be more than + // 2 * peak memory usage for alive elements. + cleanupThreshold = dependentNodes.size() * 2; + } } @Override diff --git a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/ShortInlineDependencyCache.java b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/ShortInlineDependencyCache.java index 473ceb21..72db9b32 100644 --- a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/ShortInlineDependencyCache.java +++ b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/caches/def/ShortInlineDependencyCache.java @@ -32,6 +32,14 @@ public class ShortInlineDependencyCache extends ShortInlineCacheImpl implements */ private Set> dependentNodes; + /** + * Number of elements in dependentNodes after which all the set should be checked for the presence of + * references to GC'ed objects. + * + * This threshold is required in order to evict such references as they pollute memory and never GC'ed otherwise. + */ + private int cleanupThreshold = 10; + private Reference selfReference; public ShortInlineDependencyCache(Object owner, ShortCalculatable calculable, MutableStatistics statistics) { @@ -68,6 +76,26 @@ public synchronized void trackDependency(DependencyNode node) { dependentNodes = new THashSet>(); } dependentNodes.add(node.getSelfReference()); + cleanupIfNeeded(); + } + + private void cleanupIfNeeded() { + if (dependentNodes.size() >= cleanupThreshold) { + for (Iterator> it = dependentNodes.iterator(); it.hasNext(); ) { + if (it.next().get() == null) { + it.remove(); + } + } + // It's important to increase cleanup threshold according to the number of elements in a set + // in order to maintain the balance between CPU-overhead and memory-overhead + + // The cleanup has O(N) complexity, so doing this on addition of N new elements would lead to constant + // small overhead and thus would not affect the asymptotic behaviour of operations. + + // The memory overhead could be significant but it's guaranteed that memory usage would not be more than + // 2 * peak memory usage for alive elements. + cleanupThreshold = dependentNodes.size() * 2; + } } @Override diff --git a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/resource/AbstractDependencyNode.java b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/resource/AbstractDependencyNode.java index 52460d05..89831c30 100644 --- a/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/resource/AbstractDependencyNode.java +++ b/mxcache-runtime/src/main/java/com/maxifier/mxcache/impl/resource/AbstractDependencyNode.java @@ -19,6 +19,14 @@ public abstract class AbstractDependencyNode implements DependencyNode { */ private Set> dependentNodes; + /** + * Number of elements in dependentNodes after which all the set should be checked for the presence of + * references to GC'ed objects. + * + * This threshold is required in order to evict such references as they pollute memory and never GC'ed otherwise. + */ + private int cleanupThreshold = 10; + private Reference selfReference; @Override @@ -54,9 +62,31 @@ public synchronized Reference getSelfReference() { @Override public synchronized void trackDependency(DependencyNode node) { if (dependentNodes == null) { + // this magic set is used to prevent memory leaks + // it cleans up references to GC'ed nodes on rehash dependentNodes = new THashSet>(); } dependentNodes.add(node.getSelfReference()); + cleanupIfNeeded(); + } + + private void cleanupIfNeeded() { + if (dependentNodes.size() >= cleanupThreshold) { + for (Iterator> it = dependentNodes.iterator(); it.hasNext(); ) { + if (it.next().get() == null) { + it.remove(); + } + } + // It's important to increase cleanup threshold according to the number of elements in a set + // in order to maintain the balance between CPU-overhead and memory-overhead + + // The cleanup has O(N) complexity, so doing this on addition of N new elements would lead to constant + // small overhead and thus would not affect the asymptotic behaviour of operations. + + // The memory overhead could be significant but it's guaranteed that memory usage would not be more than + // 2 * peak memory usage for alive elements. + cleanupThreshold = dependentNodes.size() * 2; + } } protected static boolean equal(@Nullable Object a, @Nullable Object b) {