You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by th...@apache.org on 2011/09/08 20:04:24 UTC
svn commit: r1166828 - in
/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk:
index/PrefixIndex.java mem/MemoryKernelImpl.java mem/NodeImpl.java
mem/NodeListSmall.java util/StringCache.java
Author: thomasm
Date: Thu Sep 8 18:04:24 2011
New Revision: 1166828
URL: http://svn.apache.org/viewvc?rev=1166828&view=rev
Log:
Conserve memory (StringCache, property value array instead of HashMap)
Added:
jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/StringCache.java
Modified:
jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/index/PrefixIndex.java
jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java
jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java
jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java
Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/index/PrefixIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/index/PrefixIndex.java?rev=1166828&r1=1166827&r2=1166828&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/index/PrefixIndex.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/index/PrefixIndex.java Thu Sep 8 18:04:24 2011
@@ -17,7 +17,6 @@
package org.apache.jackrabbit.mk.index;
import java.util.Iterator;
-import java.util.Map.Entry;
import org.apache.jackrabbit.mk.json.JsopTokenizer;
import org.apache.jackrabbit.mk.mem.NodeImpl;
@@ -39,9 +38,9 @@ public class PrefixIndex implements Inde
public void addOrRemoveNode(NodeImpl node, boolean add) {
String nodePath = node.getPath();
- for (Entry<String, String> e : node.getProperties()) {
- String propertyName = e.getKey();
- String value = e.getValue();
+ for (int i = 0, size = node.getPropertyCount(); i < size; i++) {
+ String propertyName = node.getProperty(i);
+ String value = node.getPropertyValue(i);
addOrRemoveProperty(nodePath, propertyName, value, add);
}
}
Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java?rev=1166828&r1=1166827&r2=1166828&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/MemoryKernelImpl.java Thu Sep 8 18:04:24 2011
@@ -33,7 +33,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.Map.Entry;
/*
@@ -121,8 +120,8 @@ public class MemoryKernelImpl implements
private void applyConfig(NodeImpl head) {
NodeImpl config = head.getNode("config");
- for (Entry<String, String> e : config.getProperties()) {
- nodeMap.setSetting(e.getKey(), e.getValue());
+ for (int i = 0, size = config.getPropertyCount(); i < size; i++) {
+ nodeMap.setSetting(config.getProperty(i), config.getPropertyValue(i));
}
}
Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java?rev=1166828&r1=1166827&r2=1166828&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeImpl.java Thu Sep 8 18:04:24 2011
@@ -16,18 +16,14 @@
*/
package org.apache.jackrabbit.mk.mem;
+import java.util.ArrayList;
+import java.util.Iterator;
import org.apache.jackrabbit.mk.Constants;
import org.apache.jackrabbit.mk.json.JsopBuilder;
import org.apache.jackrabbit.mk.json.JsopTokenizer;
import org.apache.jackrabbit.mk.util.ExceptionFactory;
import org.apache.jackrabbit.mk.util.PathUtils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
+import org.apache.jackrabbit.mk.util.StringCache;
/**
* An in-memory node, including all child nodes.
@@ -36,7 +32,7 @@ public class NodeImpl {
private final long revId;
private final NodeMap map;
- private HashMap<String, String> properties;
+ private String[] propertyValuePairs;
private NodeList childNodes;
private String path;
private long id;
@@ -59,8 +55,10 @@ public class NodeImpl {
return this;
}
NodeImpl clone = new NodeImpl(map, revId);
- if (properties != null) {
- clone.properties = new HashMap<String, String>(properties);
+ if (propertyValuePairs != null) {
+ String[] s = new String[propertyValuePairs.length];
+ System.arraycopy(propertyValuePairs, 0, s, 0, s.length);
+ clone.propertyValuePairs = s;
}
if (childNodes != null) {
clone.childNodes = childNodes.createClone(map, revId);
@@ -179,18 +177,54 @@ public class NodeImpl {
}
public boolean hasProperty(String propertyName) {
- return properties != null && properties.containsKey(propertyName);
+ return propertyValuePairs != null && search(propertyName, propertyValuePairs) >= 0;
+ }
+
+ /**
+ * Return the index of the key within the array of key-value pairs. If
+ * found, the method returns the index of the key, if not this method
+ * returns (- index - 2). See also Arrays.binarySearch.
+ *
+ * @param key the key
+ * @param pair the key-value pair
+ * @return the index
+ */
+ private static int search(String key, String[] pair) {
+ int low = 0;
+ int high = pair.length / 2 - 1;
+ while (low <= high) {
+ int mid = (low + high) >> 1;
+ String middle = pair[mid * 2];
+ int result = middle.compareTo(key);
+ if (result < 0) {
+ low = mid + 1;
+ } else if (result > 0) {
+ high = mid - 1;
+ } else {
+ return mid * 2;
+ }
+ }
+ // not found
+ return -(low * 2 + 2);
}
public String getProperty(String propertyName) {
- return properties == null ? null : properties.get(propertyName);
+ if (propertyValuePairs == null) {
+ return null;
+ }
+ int index = search(propertyName, propertyValuePairs);
+ if (index < 0) {
+ return null;
+ }
+ return propertyValuePairs[index + 1];
}
void append(JsopBuilder json, int depth, long offset, int count, boolean childNodeCount) {
json.object();
- if (properties != null) {
- for (Entry<String, String> e : properties.entrySet()) {
- json.key(e.getKey()).encodedValue(e.getValue());
+ String[] pv = propertyValuePairs;
+ if (pv != null) {
+ for (int i = 0, size = pv.length; i < size; i += 2) {
+ json.key(pv[i]).encodedValue(pv[i + 1]);
}
}
if (childNodes == null) {
@@ -266,17 +300,49 @@ public class NodeImpl {
}
void setProperty(String name, String value) {
- if (properties == null) {
- properties = new HashMap<String, String>();
+ propertyValuePairs = updatePair(propertyValuePairs, name, value);
+ }
+
+ private static String[] updatePair(String[] pairs, String key, String value) {
+ if (pairs == null) {
+ if (value == null) {
+ return null;
+ } else {
+ return new String[] { key, value };
+ }
}
- if (value == null) {
- properties.remove(name);
- if (properties.size() == 0) {
- properties = null;
+ int index = search(key, pairs);
+ if (index < 0) {
+ if (value == null) {
+ return pairs;
}
- } else {
- properties.put(name, value);
+ index = -index - 2;
+ String[] newPairs = new String[pairs.length + 2];
+ if (index > 0) {
+ System.arraycopy(pairs, 0, newPairs, 0, index);
+ }
+ int len = newPairs.length - index;
+ if (len > 2) {
+ System.arraycopy(pairs, index, newPairs, index + 2, len - 2);
+ }
+ pairs = newPairs;
+ pairs[index] = StringCache.cache(key);
+ } else if (value == null) {
+ if (pairs.length == 2) {
+ return null;
+ }
+ String[] newPairs = new String[pairs.length - 2];
+ if (index > 0) {
+ System.arraycopy(pairs, 0, newPairs, 0, index);
+ }
+ int len = newPairs.length - index;
+ if (len > 0) {
+ System.arraycopy(pairs, index + 2, newPairs, index, len);
+ }
+ return newPairs;
}
+ pairs[index + 1] = StringCache.cache(value);
+ return pairs;
}
public static NodeImpl parse(NodeMap map, JsopTokenizer t, long revId) {
@@ -322,12 +388,16 @@ public class NodeImpl {
return childNodes.getNames(0, maxCount);
}
- public Iterable<Entry<String, String>> getProperties() {
- if (properties == null) {
- Map<String, String> e = Collections.emptyMap();
- return e.entrySet();
- }
- return properties.entrySet();
+ public int getPropertyCount() {
+ return propertyValuePairs == null ? 0 : propertyValuePairs.length / 2;
+ }
+
+ public String getProperty(int index) {
+ return propertyValuePairs[index + index];
+ }
+
+ public String getPropertyValue(int index) {
+ return propertyValuePairs[index + index + 1];
}
public String toString() {
@@ -343,9 +413,10 @@ public class NodeImpl {
json.setLineLength(120);
json.encodedValue(map.formatId(id)).appendTag("=");
json.object();
- if (properties != null && properties.size() > 0) {
- for (Entry<String, String> e : properties.entrySet()) {
- json.key(e.getKey()).encodedValue(e.getValue());
+ String[] pv = propertyValuePairs;
+ if (pv != null) {
+ for (int i = 0, size = pv.length; i < size; i += 2) {
+ json.key(pv[i]).encodedValue(pv[i + 1]);
}
}
if (childNodes != null && childNodes.size() > 0) {
Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java?rev=1166828&r1=1166827&r2=1166828&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/mem/NodeListSmall.java Thu Sep 8 18:04:24 2011
@@ -21,6 +21,7 @@ import org.apache.jackrabbit.mk.json.Jso
import org.apache.jackrabbit.mk.mem.NodeImpl.ChildVisitor;
import org.apache.jackrabbit.mk.util.ExceptionFactory;
import org.apache.jackrabbit.mk.util.IOUtils;
+import org.apache.jackrabbit.mk.util.StringCache;
import org.apache.jackrabbit.mk.util.StringUtils;
public class NodeListSmall implements NodeList {
@@ -90,6 +91,7 @@ public class NodeListSmall implements No
throw ExceptionFactory.get("Node already exists: " + name);
}
index = -index - 1;
+ name = StringCache.cache(name);
names = StringUtils.arrayInsert(names, size, name);
children = StringUtils.arrayInsert(children, size, x);
sort = StringUtils.arrayInsert(sort, index, size);
Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/StringCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/StringCache.java?rev=1166828&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/StringCache.java (added)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/StringCache.java Thu Sep 8 18:04:24 2011
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.mk.util;
+
+import java.lang.ref.SoftReference;
+
+/**
+ * A simple string cache.
+ */
+public class StringCache {
+
+ public static final boolean OBJECT_CACHE = getBooleanSetting("mk.objectCache", true);
+ public static final int OBJECT_CACHE_SIZE = IOUtils.nextPowerOf2(getIntSetting("mk.objectCacheSize", 1024));
+
+ private static SoftReference<String[]> softCache = new SoftReference<String[]>(null);
+
+ private StringCache() {
+ // utility class
+ }
+
+ private static boolean getBooleanSetting(String name, boolean defaultValue) {
+ String s = getProperty(name);
+ if (s != null) {
+ try {
+ return Boolean.valueOf(s).booleanValue();
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+ }
+ return defaultValue;
+ }
+
+ private static int getIntSetting(String name, int defaultValue) {
+ String s = getProperty(name);
+ if (s != null) {
+ try {
+ return Integer.decode(s).intValue();
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+ }
+ return defaultValue;
+ }
+
+ private static String getProperty(String name) {
+ try {
+ return System.getProperty(name);
+ } catch (Exception e) {
+ // SecurityException
+ // applets may not do that - ignore
+ return null;
+ }
+ }
+
+ private static String[] getCache() {
+ String[] cache;
+ // softCache can be null due to a Tomcat problem
+ // a workaround is disable the system property org.apache.
+ // catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES
+ if (softCache != null) {
+ cache = softCache.get();
+ if (cache != null) {
+ return cache;
+ }
+ }
+ try {
+ cache = new String[OBJECT_CACHE_SIZE];
+ } catch (OutOfMemoryError e) {
+ return null;
+ }
+ softCache = new SoftReference<String[]>(cache);
+ return cache;
+ }
+
+ /**
+ * Get the string from the cache if possible. If the string has not been
+ * found, it is added to the cache. If there is such a string in the cache,
+ * that one is returned.
+ *
+ * @param s the original string
+ * @return a string with the same content, if possible from the cache
+ */
+ public static String cache(String s) {
+ if (!OBJECT_CACHE) {
+ return s;
+ }
+ if (s == null) {
+ return s;
+ } else if (s.length() == 0) {
+ return "";
+ }
+ int hash = s.hashCode();
+ String[] cache = getCache();
+ if (cache != null) {
+ int index = hash & (OBJECT_CACHE_SIZE - 1);
+ String cached = cache[index];
+ if (cached != null) {
+ if (s.equals(cached)) {
+ return cached;
+ }
+ }
+ cache[index] = s;
+ }
+ return s;
+ }
+
+ /**
+ * Get a string from the cache, and if no such string has been found, create
+ * a new one with only this content. This solves out of memory problems if
+ * the string is a substring of another, large string. In Java, strings are
+ * shared, which could lead to memory problems. This avoid such problems.
+ *
+ * @param s the string
+ * @return a string that is guaranteed not be a substring of a large string
+ */
+ public static String fromCacheOrNew(String s) {
+ if (!OBJECT_CACHE) {
+ return s;
+ }
+ if (s == null) {
+ return s;
+ } else if (s.length() == 0) {
+ return "";
+ }
+ int hash = s.hashCode();
+ String[] cache = getCache();
+ int index = hash & (OBJECT_CACHE_SIZE - 1);
+ if (cache == null) {
+ return s;
+ }
+ String cached = cache[index];
+ if (cached != null) {
+ if (s.equals(cached)) {
+ return cached;
+ }
+ }
+ // create a new object that is not shared
+ // (to avoid out of memory if it is a substring of a big String)
+ // NOPMD
+ s = new String(s);
+ cache[index] = s;
+ return s;
+ }
+
+ /**
+ * Clear the cache. This method is used for testing.
+ */
+ public static void clearCache() {
+ softCache = new SoftReference<String[]>(null);
+ }
+
+}