Author: cutting Date: Wed Feb 21 12:32:51 2007 New Revision: 510188 URL: http://svn.apache.org/viewvc?view=rev&rev=510188 Log: HADOOP-1017. Cache constructors. Contributed by Ron Bodkin.
Added: lucene/hadoop/trunk/src/test/org/apache/hadoop/util/ lucene/hadoop/trunk/src/test/org/apache/hadoop/util/TestReflectionUtils.java Modified: lucene/hadoop/trunk/CHANGES.txt lucene/hadoop/trunk/src/java/org/apache/hadoop/util/ReflectionUtils.java Modified: lucene/hadoop/trunk/CHANGES.txt URL: http://svn.apache.org/viewvc/lucene/hadoop/trunk/CHANGES.txt?view=diff&rev=510188&r1=510187&r2=510188 ============================================================================== --- lucene/hadoop/trunk/CHANGES.txt (original) +++ lucene/hadoop/trunk/CHANGES.txt Wed Feb 21 12:32:51 2007 @@ -78,6 +78,9 @@ namenode and jobtracker with include and exclude files. (Wendy Chien via cutting) +24. HADOOP-1017. Cache constructors, for improved performance. + (Ron Bodkin via cutting) + Release 0.11.2 - 2007-02-16 Modified: lucene/hadoop/trunk/src/java/org/apache/hadoop/util/ReflectionUtils.java URL: http://svn.apache.org/viewvc/lucene/hadoop/trunk/src/java/org/apache/hadoop/util/ReflectionUtils.java?view=diff&rev=510188&r1=510187&r2=510188 ============================================================================== --- lucene/hadoop/trunk/src/java/org/apache/hadoop/util/ReflectionUtils.java (original) +++ lucene/hadoop/trunk/src/java/org/apache/hadoop/util/ReflectionUtils.java Wed Feb 21 12:32:51 2007 @@ -21,6 +21,8 @@ import java.lang.reflect.Constructor; import java.io.*; import java.lang.management.*; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.logging.Log; import org.apache.hadoop.conf.*; @@ -33,6 +35,12 @@ public class ReflectionUtils { private static final Class[] emptyArray = new Class[]{}; + /** + * Cache of constructors for each class. Pins the classes so they + * can't be garbage collected until ReflectionUtils can be collected. + */ + private static final Map<Class<?>, Constructor<?>> CONSTRUCTOR_CACHE = + new ConcurrentHashMap<Class<?>, Constructor<?>>(); /** * Check and set 'configuration' if necessary. @@ -61,8 +69,12 @@ public static Object newInstance(Class theClass, Configuration conf) { Object result; try { - Constructor meth = theClass.getDeclaredConstructor(emptyArray); - meth.setAccessible(true); + Constructor meth = CONSTRUCTOR_CACHE.get(theClass); + if (meth == null) { + meth = theClass.getDeclaredConstructor(emptyArray); + meth.setAccessible(true); + CONSTRUCTOR_CACHE.put(theClass, meth); + } result = meth.newInstance(); } catch (Exception e) { throw new RuntimeException(e); @@ -152,4 +164,14 @@ } } } + + // methods to support testing + static void clearCache() { + CONSTRUCTOR_CACHE.clear(); + } + + static int getCacheSize() { + return CONSTRUCTOR_CACHE.size(); + } + } Added: lucene/hadoop/trunk/src/test/org/apache/hadoop/util/TestReflectionUtils.java URL: http://svn.apache.org/viewvc/lucene/hadoop/trunk/src/test/org/apache/hadoop/util/TestReflectionUtils.java?view=auto&rev=510188 ============================================================================== --- lucene/hadoop/trunk/src/test/org/apache/hadoop/util/TestReflectionUtils.java (added) +++ lucene/hadoop/trunk/src/test/org/apache/hadoop/util/TestReflectionUtils.java Wed Feb 21 12:32:51 2007 @@ -0,0 +1,91 @@ +package org.apache.hadoop.util; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; + +import junit.framework.TestCase; + +public class TestReflectionUtils extends TestCase { + + private static Class toConstruct[] = { String.class, TestReflectionUtils.class, HashMap.class }; + private Throwable failure = null; + + public void setUp() { + ReflectionUtils.clearCache(); + } + + public void testCache() throws Exception { + assertEquals(0, cacheSize()); + doTestCache(); + assertEquals(toConstruct.length, cacheSize()); + ReflectionUtils.clearCache(); + assertEquals(0, cacheSize()); + } + + + private void doTestCache() { + for (int i=0; i<toConstruct.length; i++) { + Class cl = toConstruct[i]; + Object x = ReflectionUtils.newInstance(cl, null); + Object y = ReflectionUtils.newInstance(cl, null); + assertEquals(cl, x.getClass()); + assertEquals(cl, y.getClass()); + } + } + + public void testThreadSafe() throws Exception { + Thread[] th = new Thread[32]; + for (int i=0; i<th.length; i++) { + th[i] = new Thread() { + public void run() { + try { + doTestCache(); + } catch (Throwable t) { + failure = t; + } + } + }; + th[i].start(); + } + for (int i=0; i<th.length; i++) { + th[i].join(); + } + if (failure != null) { + failure.printStackTrace(); + fail(failure.getMessage()); + } + } + + private int cacheSize() throws Exception { + return ReflectionUtils.getCacheSize(); + } + + public void testCantCreate() { + try { + ReflectionUtils.newInstance(NoDefaultCtor.class, null); + fail("invalid call should fail"); + } catch (RuntimeException rte) { + assertEquals(NoSuchMethodException.class, rte.getCause().getClass()); + } + } + + public void testCacheDoesntLeak() throws Exception { + int iterations=9999; // very fast, but a bit less reliable - bigger numbers force GC + for (int i=0; i<iterations; i++) { + URLClassLoader loader = new URLClassLoader(new URL[0], getClass().getClassLoader()); + Class cl = Class.forName("org.apache.hadoop.util.TestReflectionUtils$LoadedInChild", false, loader); + Object o = ReflectionUtils.newInstance(cl, null); + assertEquals(cl, o.getClass()); + } + System.gc(); + assertTrue(cacheSize()+" too big", cacheSize()<iterations); + } + + private static class LoadedInChild { + } + + public static class NoDefaultCtor { + public NoDefaultCtor(int x) {} + } +}