You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by mr...@apache.org on 2013/03/04 11:46:57 UTC

svn commit: r1452248 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel: KernelNodeState.java KernelNodeStore.java

Author: mreutegg
Date: Mon Mar  4 10:46:57 2013
New Revision: 1452248

URL: http://svn.apache.org/r1452248
Log:
OAK-643: Very high memory usage with 6000 child nodes
- limit cache size by memory using a Weigher

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java?rev=1452248&r1=1452247&r2=1452248&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeState.java Mon Mar  4 10:46:57 2013
@@ -152,6 +152,10 @@ public final class KernelNodeState exten
                 initialized = true;
             }
         }
+        if (initialized) {
+            // refresh cache to force re-calculation of weight (OAK-643)
+            cache.refresh(revision + path);
+        }
         if (initialized && !PathUtils.denotesRoot(path)) {
             // OAK-591: check if we can re-use a previous revision
             // by looking up the node state by hash or id (if available)
@@ -344,6 +348,46 @@ public final class KernelNodeState exten
         return path;
     }
 
+    /**
+     * @return the approximate memory usage of this node state.
+     */
+    synchronized int getMemory() {
+        // base memory footprint is roughly 64 bytes
+        int memory = 64;
+        // path String
+        memory += 12 + path.length() * 2;
+        // revision String
+        memory += 12 + revision.length() * 2;
+        // optional hash String
+        if (hash != null) {
+            memory += 12 + hash.length() * 2;
+        }
+        // optional id String
+        if (id != null && !id.equals(hash)) {
+            memory += 12 + id.length() * 2;
+        }
+        // rough approximation for properties
+        if (properties != null) {
+            for (Map.Entry<String, PropertyState> entry : properties.entrySet()) {
+                // name
+                memory += 12 + entry.getKey().length() * 2;
+                PropertyState propState = entry.getValue();
+                if (propState.getType() != Type.BINARY
+                        && propState.getType() != Type.BINARIES) {
+                    // assume binaries go into blob store
+                    for (int i = 0; i < propState.count(); i++) {
+                        memory += propState.size(i);
+                    }
+                }
+            }
+        }
+        // rough approximation for child nodes
+        if (childNames != null) {
+            memory += childNames.size() * 150;
+        }
+        return memory;
+    }
+
     //------------------------------------------------------------< private >---
 
     private boolean hasChanges(String journal) {

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java?rev=1452248&r1=1452247&r2=1452248&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/kernel/KernelNodeStore.java Mon Mar  4 10:46:57 2013
@@ -25,6 +25,10 @@ import javax.annotation.Nonnull;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
+import com.google.common.cache.Weigher;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+
 import org.apache.jackrabbit.mk.api.MicroKernel;
 import org.apache.jackrabbit.mk.api.MicroKernelException;
 import org.apache.jackrabbit.oak.spi.commit.EmptyObserver;
@@ -52,17 +56,33 @@ public class KernelNodeStore implements 
     private volatile Observer observer = EmptyObserver.INSTANCE;
 
     private final LoadingCache<String, KernelNodeState> cache =
-            CacheBuilder.newBuilder().maximumSize(10000).build(
-                    new CacheLoader<String, KernelNodeState>() {
-                        @Override
-                        public KernelNodeState load(String key) {
-                            int slash = key.indexOf('/');
-                            String revision = key.substring(0, slash);
-                            String path = key.substring(slash);
-                            return new KernelNodeState(
-                                    kernel, path, revision, cache);
-                        }
-                    });
+            CacheBuilder.newBuilder().maximumWeight(16 * 1024 * 1024).weigher(
+                    new Weigher<String, KernelNodeState>() {
+                @Override
+                public int weigh(String key, KernelNodeState state) {
+                    return state.getMemory();
+                }
+            }).build(new CacheLoader<String, KernelNodeState>() {
+                @Override
+                public KernelNodeState load(String key) {
+                    int slash = key.indexOf('/');
+                    String revision = key.substring(0, slash);
+                    String path = key.substring(slash);
+                    return new KernelNodeState(kernel, path, revision, cache);
+                }
+
+                @Override
+                public ListenableFuture<KernelNodeState> reload(String key,
+                                                                KernelNodeState oldValue)
+                        throws Exception {
+                    // LoadingCache.reload() is only used to re-calculate the
+                    // memory usage on KernelNodeState.init(). Therefore
+                    // we simply return the old value as is (OAK-643)
+                    SettableFuture<KernelNodeState> future = SettableFuture.create();
+                    future.set(oldValue);
+                    return future;
+                }
+            });
 
     /**
      * State of the current root node.