You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2019/07/05 22:45:36 UTC
[commons-bcel] branch master updated: [BCEL-317] Pluggable cache
for ConstantUtf8.
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-bcel.git
The following commit(s) were added to refs/heads/master by this push:
new e9853a3 [BCEL-317] Pluggable cache for ConstantUtf8.
e9853a3 is described below
commit e9853a31939c2bdbd15bc321804d7cdc6a4ec995
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Fri Jul 5 18:45:33 2019 -0400
[BCEL-317] Pluggable cache for ConstantUtf8.
---
src/changes/changes.xml | 1 +
.../org/apache/bcel/classfile/ConstantUtf8.java | 174 +++++++++++++--------
2 files changed, 112 insertions(+), 63 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 0c2ef97..87667f2 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -67,6 +67,7 @@ The <action> type attribute can be add,update,fix,remove.
<action issue="BCEL-278" type="fix" dev="ggregory" due-to="Valery Barysoky">InvokeInstruction.toString(final ConstantPool cp) throws NoSuchElementException #11.</action>
<action issue="BCEL-318" type="fix" dev="ggregory" due-to="Gary Gregory">Add org.apache.bcel.classfile.ConstantUtf8.clearCache().</action>
<action type="update" dev="ggregory" due-to="Gary Gregory">Update tests from JNA 4.5.0 to 5.3.1.</action>
+ <action issue="BCEL-317" type="update" dev="ggregory" due-to="Tomo Suzuki, Gary Gregory">Pluggable cache for ConstantUtf8.</action>
</release>
<release version="6.3.1" date="2019-03-20" description="Bug fix release">
diff --git a/src/main/java/org/apache/bcel/classfile/ConstantUtf8.java b/src/main/java/org/apache/bcel/classfile/ConstantUtf8.java
index e02fed1..21700fb 100644
--- a/src/main/java/org/apache/bcel/classfile/ConstantUtf8.java
+++ b/src/main/java/org/apache/bcel/classfile/ConstantUtf8.java
@@ -27,44 +27,73 @@ import java.util.Map;
import org.apache.bcel.Const;
/**
- * This class is derived from the abstract {@link Constant}
- * and represents a reference to a Utf8 encoded string.
- *
- * @see Constant
+ * Extends the abstract {@link Constant} to represent a reference to a UTF-8 encoded string.
+ * <p>
+ * The following system properties govern caching this class performs.
+ * <ul>
+ * <li>{@value #SYS_PROP_CACHE_MAX_ENTRIES} (since 6.4): The size of the cache, by default 0, meaning caching is disabled.</li>
+ * <li>{@value #SYS_PROP_CACHE_MAX_ENTRY_SIZE} (since 6.0): The maximum size of the values to cache, by default 200, 0 disables
+ * caching. Values larger than this are <em>not</em> cached.</li>
+ * <li>{@value #SYS_PROP_STATISTICS} (since 6.0): Prints statistics on the console when the JVM exits.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Here is a sample Maven invocation with caching disabled:
+ * </p>
+ *
+ * <pre>
+ * mvn test -Dbcel.statistics=true -Dbcel.maxcached.size=0 -Dbcel.maxcached=0
+ * </pre>
+ * <p>
+ * Here is a sample Maven invocation with caching enabled:
+ * </p>
+ *
+ * <pre>
+ * mvn test -Dbcel.statistics=true -Dbcel.maxcached.size=100000 -Dbcel.maxcached=5000000
+ * </pre>
+ *
+ * @see Constant
*/
public final class ConstantUtf8 extends Constant {
- private static class CacheHolder {
+ private static class Cache {
- private static final int MAX_CACHE_ENTRIES = 20000;
- private static final int INITIAL_CACHE_CAPACITY = (int) (MAX_CACHE_ENTRIES / 0.75);
+ private static final boolean BCEL_STATISTICS = Boolean.getBoolean(SYS_PROP_STATISTICS);
+ private static final int MAX_ENTRIES = Integer.getInteger(SYS_PROP_CACHE_MAX_ENTRIES, 0).intValue();
+ private static final int INITIAL_CAPACITY = (int) (MAX_ENTRIES / 0.75);
private static final HashMap<String, ConstantUtf8> CACHE = new LinkedHashMap<String, ConstantUtf8>(
- INITIAL_CACHE_CAPACITY, 0.75f, true) {
-
+ INITIAL_CAPACITY, 0.75f, true) {
+
private static final long serialVersionUID = -8506975356158971766L;
@Override
protected boolean removeEldestEntry(final Map.Entry<String, ConstantUtf8> eldest) {
- return size() > MAX_CACHE_ENTRIES;
+ return size() > MAX_ENTRIES;
}
};
+ // Set the size to 0 or below to skip caching entirely
+ private static final int MAX_ENTRY_SIZE = Integer.getInteger(SYS_PROP_CACHE_MAX_ENTRY_SIZE, 200).intValue();
+
+ static boolean isEnabled() {
+ return Cache.MAX_ENTRIES > 0 && MAX_ENTRY_SIZE > 0;
+ }
+
}
// TODO these should perhaps be AtomicInt?
private static volatile int considered = 0;
+ private static volatile int created = 0;
private static volatile int hits = 0;
private static volatile int skipped = 0;
- private static volatile int created = 0;
-
- // Set the size to 0 or below to skip caching entirely
- private static final int MAX_CACHED_SIZE = Integer.getInteger("bcel.maxcached.size", 200).intValue();
- private static final boolean BCEL_STATISTICS = Boolean.getBoolean("bcel.statistics");
+ private static final String SYS_PROP_CACHE_MAX_ENTRIES = "bcel.maxcached";
+ private static final String SYS_PROP_CACHE_MAX_ENTRY_SIZE = "bcel.maxcached.size";
+ private static final String SYS_PROP_STATISTICS = "bcel.statistics";
static {
- if (BCEL_STATISTICS) {
+ if (Cache.BCEL_STATISTICS) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
@@ -79,141 +108,160 @@ public final class ConstantUtf8 extends Constant {
*
* @since 6.4.0
*/
- public static void clearCache() {
- CacheHolder.CACHE.clear();
+ public synchronized static void clearCache() {
+ Cache.CACHE.clear();
}
-
+
// for accesss by test code
- static void clearStats() {
+ static synchronized void clearStats() {
hits = considered = skipped = created = 0;
}
/**
+ * Gets a new or cached instance of the given value.
+ * <p>
+ * See {@link ConstantUtf8} class Javadoc for details.
+ * </p>
+ *
+ * @param value the value.
+ * @return a new or cached instance of the given value.
* @since 6.0
*/
- public static ConstantUtf8 getCachedInstance(final String s) {
- if (s.length() > MAX_CACHED_SIZE) {
+ public static ConstantUtf8 getCachedInstance(final String value) {
+ if (value.length() > Cache.MAX_ENTRY_SIZE) {
skipped++;
- return new ConstantUtf8(s);
+ return new ConstantUtf8(value);
}
considered++;
synchronized (ConstantUtf8.class) { // might be better with a specific lock object
- ConstantUtf8 result = CacheHolder.CACHE.get(s);
+ ConstantUtf8 result = Cache.CACHE.get(value);
if (result != null) {
hits++;
return result;
}
- result = new ConstantUtf8(s);
- CacheHolder.CACHE.put(s, result);
+ result = new ConstantUtf8(value);
+ Cache.CACHE.put(value, result);
return result;
}
}
/**
+ * Gets a new or cached instance of the given value.
+ * <p>
+ * See {@link ConstantUtf8} class Javadoc for details.
+ * </p>
+ *
+ * @param dataInput the value.
+ * @return a new or cached instance of the given value.
+ * @throws IOException if an I/O error occurs.
* @since 6.0
*/
- public static ConstantUtf8 getInstance (final DataInput input) throws IOException {
- return getInstance(input.readUTF());
+ public static ConstantUtf8 getInstance(final DataInput dataInput) throws IOException {
+ return getInstance(dataInput.readUTF());
}
/**
+ * Gets a new or cached instance of the given value.
+ * <p>
+ * See {@link ConstantUtf8} class Javadoc for details.
+ * </p>
+ *
+ * @param value the value.
+ * @return a new or cached instance of the given value.
* @since 6.0
*/
- public static ConstantUtf8 getInstance(final String s) {
- return new ConstantUtf8(s);
+ public static ConstantUtf8 getInstance(final String value) {
+ return Cache.isEnabled() ? getCachedInstance(value) : new ConstantUtf8(value);
}
// for accesss by test code
static void printStats() {
- System.err.printf("Cache hit %,d/%,d, %d skipped.%n",hits,considered,skipped);
- System.err.printf("Total of %,d ConstantUtf8 objects created.%n", created);
+ final String prefix = "[Apache Commons BCEL]";
+ System.err.printf("%s Cache hit %,d/%,d, %d skipped.%n", prefix, hits, considered, skipped);
+ System.err.printf("%s Total of %,d ConstantUtf8 objects created.%n", prefix, created);
+ System.err.printf("%s Configuration: %s=%,d, %s=%,d.%n", prefix, SYS_PROP_CACHE_MAX_ENTRIES, Cache.MAX_ENTRIES,
+ SYS_PROP_CACHE_MAX_ENTRY_SIZE, Cache.MAX_ENTRY_SIZE);
}
- private final String bytes;
+ private final String value;
/**
- * Initialize from another object.
+ * Initializes from another object.
+ *
+ * @param constantUtf8 the value.
*/
- public ConstantUtf8(final ConstantUtf8 c) {
- this(c.getBytes());
+ public ConstantUtf8(final ConstantUtf8 constantUtf8) {
+ this(constantUtf8.getBytes());
}
-
/**
- * Initialize instance from file data.
+ * Initializes instance from file data.
*
- * @param file Input stream
+ * @param dataInput Input stream
* @throws IOException
*/
- ConstantUtf8(final DataInput file) throws IOException {
+ ConstantUtf8(final DataInput dataInput) throws IOException {
super(Const.CONSTANT_Utf8);
- bytes = file.readUTF();
+ value = dataInput.readUTF();
created++;
}
-
/**
- * @param bytes Data
+ * @param value Data
*/
- public ConstantUtf8(final String bytes) {
+ public ConstantUtf8(final String value) {
super(Const.CONSTANT_Utf8);
- if (bytes == null) {
- throw new IllegalArgumentException("bytes must not be null!");
+ if (value == null) {
+ throw new IllegalArgumentException("Value must not be null.");
}
- this.bytes = bytes;
+ this.value = value;
created++;
}
-
/**
- * Called by objects that are traversing the nodes of the tree implicitely
- * defined by the contents of a Java class. I.e., the hierarchy of methods,
- * fields, attributes, etc. spawns a tree of objects.
+ * Called by objects that are traversing the nodes of the tree implicitely defined by the contents of a Java class.
+ * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
*
* @param v Visitor object
*/
@Override
- public void accept( final Visitor v ) {
+ public void accept(final Visitor v) {
v.visitConstantUtf8(this);
}
-
/**
- * Dump String in Utf8 format to file stream.
+ * Dumps String in Utf8 format to file stream.
*
* @param file Output file stream
* @throws IOException
*/
@Override
- public final void dump( final DataOutputStream file ) throws IOException {
+ public final void dump(final DataOutputStream file) throws IOException {
file.writeByte(super.getTag());
- file.writeUTF(bytes);
+ file.writeUTF(value);
}
-
/**
* @return Data converted to string.
*/
public final String getBytes() {
- return bytes;
+ return value;
}
-
/**
- * @param bytes the raw bytes of this Utf-8
+ * @param bytes the raw bytes of this UTF-8
* @deprecated (since 6.0)
*/
@java.lang.Deprecated
- public final void setBytes( final String bytes ) {
+ public final void setBytes(final String bytes) {
throw new UnsupportedOperationException();
}
-
/**
* @return String representation
*/
@Override
public final String toString() {
- return super.toString() + "(\"" + Utility.replace(bytes, "\n", "\\n") + "\")";
+ return super.toString() + "(\"" + Utility.replace(value, "\n", "\\n") + "\")";
}
}