You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2009/11/09 23:16:28 UTC
svn commit: r834257 - in
/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl:
ClassCreator.java ClassCreatorTest.java
Author: henrib
Date: Mon Nov 9 22:16:28 2009
New Revision: 834257
URL: http://svn.apache.org/viewvc?rev=834257&view=rev
Log:
Fix for JEXL-61; all caches get wrapped by SoftReference, allowing them to be GCed under memory pressure.
Added:
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreator.java (with props)
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreatorTest.java (with props)
Added: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreator.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreator.java?rev=834257&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreator.java (added)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreator.java Mon Nov 9 22:16:28 2009
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2009 henri.
+ *
+ * Licensed 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.
+ * under the License.
+ */
+package org.apache.commons.jexl;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * Helper class to test GC / reference interactions.
+ * Dynamically creates a class by compiling generated source Java code and
+ * load it through a dedicated class loader.
+ */
+public class ClassCreator {
+ private final File base;
+ private File packageDir = null;
+ private int seed = 0;
+ private String className = null;
+ private String sourceName = null;
+ private ClassLoader loader = null;
+
+ public ClassCreator(File theBase) throws Exception {
+ base = theBase;
+ }
+
+ public void clear() {
+ seed = 0;
+ packageDir = null;
+ className = null;
+ sourceName = null;
+ packageDir = null;
+ loader = null;
+ }
+
+ public void setSeed(int s) {
+ seed = s;
+ className = "foo" + s;
+ sourceName = className + ".java";
+ packageDir = new File(base, seed + "/org/apache/commons/jexl/generated");
+ packageDir.mkdirs();
+ loader = null;
+ }
+
+ public String getClassName() {
+ return "org.apache.commons.jexl.generated." + className;
+ }
+
+ public Class<?> getClassInstance() throws Exception {
+ return getClassLoader().loadClass("org.apache.commons.jexl.generated." + className);
+ }
+
+ public ClassLoader getClassLoader() throws Exception {
+ if (loader == null) {
+ URL classpath = (new File(base, Integer.toString(seed))).toURI().toURL();
+ loader = new URLClassLoader(new URL[]{classpath}, null);
+ }
+ return loader;
+ }
+
+ public Class<?> createClass() throws Exception {
+ // generate, compile & validate
+ generate();
+ Class<?> clazz = compile();
+ if (clazz == null) {
+ throw new Exception("failed to compile foo" + seed);
+ }
+ Object v = validate(clazz);
+ if (v instanceof Integer && ((Integer) v).intValue() == seed) {
+ return clazz;
+ }
+ throw new Exception("failed to validate foo" + seed);
+ }
+
+ void generate() throws Exception {
+ FileWriter aWriter = new FileWriter(new File(packageDir, sourceName), false);
+ aWriter.write("package org.apache.commons.jexl.generated;");
+ aWriter.write("public class " + className + "{\n");
+ aWriter.write("private int value =");
+ aWriter.write(Integer.toString(seed));
+ aWriter.write(";\n");
+ aWriter.write(" public void setValue(int v) {");
+ aWriter.write(" value = v;");
+ aWriter.write(" }\n");
+ aWriter.write(" public int getValue() {");
+ aWriter.write(" return value;");
+ aWriter.write(" }\n");
+ aWriter.write(" }\n");
+ aWriter.flush();
+ aWriter.close();
+ }
+
+ Class<?> compile() throws Exception {
+ String[] source = {packageDir.getPath() + "/" + sourceName};
+ if (com.sun.tools.javac.Main.compile(source) >= 0) {
+ return getClassLoader().loadClass("org.apache.commons.jexl.generated." + className);
+ }
+ return null;
+ }
+
+ Object validate(Class<?> clazz) throws Exception {
+ Class<?> params[] = {};
+ Object paramsObj[] = {};
+ Object iClass = clazz.newInstance();
+ Method thisMethod = clazz.getDeclaredMethod("getValue", params);
+ return thisMethod.invoke(iClass, paramsObj);
+ }
+}
Propchange: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreator.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreatorTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreatorTest.java?rev=834257&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreatorTest.java (added)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreatorTest.java Mon Nov 9 22:16:28 2009
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2009 henri.
+ *
+ * Licensed 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.
+ * under the License.
+ */
+package org.apache.commons.jexl;
+
+import java.io.File;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Basic check on automated class creation
+ */
+public class ClassCreatorTest extends JexlTestCase {
+ static final Log logger = LogFactory.getLog(JexlTestCase.class);
+ static final int LOOPS = 8;
+ private File base = null;
+
+ @Override
+ public void setUp() throws Exception {
+ base = new File(System.getProperty("java.io.tmpdir")
+ + File.pathSeparator
+ + "jexl"
+ + System.currentTimeMillis());
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ deleteDirectory(base);
+ }
+
+ private void deleteDirectory(File dir) {
+ if (dir.isDirectory()) {
+ for (File file : dir.listFiles()) {
+ if (file.isFile()) {
+ file.delete();
+ }
+ }
+ }
+ dir.delete();
+ }
+
+ // A space hog class
+ static final int MEGA = 1024 * 1024;
+ public class BigObject {
+ private final byte[] space = new byte[MEGA];
+ private final int id;
+
+ public BigObject(int id) {
+ this.id = id;
+ }
+
+ public int getId() {
+ return id;
+ }
+ }
+
+ // A soft reference on class
+ static final class ClassReference extends WeakReference<Class<?>> {
+ ClassReference(Class<?> clazz, ReferenceQueue<Object> queue) {
+ super(clazz, queue);
+ }
+ }
+ // A weak reference on instance
+ static final class InstanceReference extends SoftReference<Object> {
+ InstanceReference(Object obj, ReferenceQueue<Object> queue) {
+ super(obj, queue);
+ }
+ }
+
+ public void testOne() throws Exception {
+ ClassCreator cctor = new ClassCreator(base);
+ cctor.setSeed(1);
+ Class<?> foo1 = cctor.createClass();
+ assertEquals("foo1", foo1.getSimpleName());
+ cctor.clear();
+ }
+
+ public void testMany() throws Exception {
+ int pass = 0;
+ int gced = -1;
+ ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+ List<Reference<?>> stuff = new ArrayList<Reference<?>>();
+ // keeping a reference on methods prevent classes from being GCed
+// List<Object> mm = new ArrayList<Object>();
+ JexlEngine jexl = new JexlEngine();
+ jexl.setCache(512);
+ Expression expr = jexl.createExpression("foo.value");
+ Expression newx = jexl.createExpression("foo = new(clazz)");
+ JexlContext context = JexlHelper.createContext();
+
+ ClassCreator cctor = new ClassCreator(base);
+ for (int i = 0; i < LOOPS && gced < 0; ++i) {
+ cctor.setSeed(i);
+ Class<?> clazz;
+ if (pass ==0) {
+ clazz = cctor.createClass();
+ } else {
+ clazz = cctor.getClassInstance();
+ if (clazz == null) {
+ assertEquals(i, gced);
+ break;
+ }
+ }
+ // this code verifies the assumption that holding a strong reference to a method prevents
+ // its owning class from being GCed
+// Method m = clazz.getDeclaredMethod("getValue", new Class<?>[0]);
+// mm.add(m);
+ // we should not be able to create foox since it is unknown to the Jexl classloader
+ context.getVars().put("clazz", cctor.getClassName());
+ context.getVars().put("foo", null);
+ Object z = newx.evaluate(context);
+ assertNull(z);
+ // check with the class itself
+ context.getVars().put("clazz", clazz);
+ z = newx.evaluate(context);
+ assertNotNull(clazz + ": class " + i + " could not be instantiated on pass " + pass, z);
+ assertEquals(new Integer(i), expr.evaluate(context));
+ // with the proper class loader, attempt to create an instance from the class name
+ jexl.setClassLoader(cctor.getClassLoader());
+ z = newx.evaluate(context);
+ assertTrue(z.getClass().equals(clazz));
+ assertEquals(new Integer(i), expr.evaluate(context));
+ cctor.clear();
+ jexl.setClassLoader(null);
+
+ // on pass 0, attempt to force GC to run and collect generated classes
+ if (pass == 0) {
+ // add a weak reference on the class
+ stuff.add(new ClassReference(clazz, queue));
+ // add a soft reference on an instance
+ stuff.add(new InstanceReference(clazz.newInstance(), queue));
+
+ // attempt to force GC:
+ // while we still have a MB free, create & store big objects
+ for (int b = 0; b < 64 && Runtime.getRuntime().freeMemory() > MEGA; ++b) {
+ BigObject big = new BigObject(b);
+ stuff.add(new InstanceReference(big, queue));
+ }
+ // hint it...
+ System.gc();
+ // let's see if some weak refs got collected
+ boolean qr = false;
+ while (queue.poll() != null) {
+ Reference<?> ref = queue.remove(1);
+ if (ref instanceof ClassReference) {
+ gced = i;
+ qr = true;
+ }
+ }
+ if (qr) {
+ //logger.warn("may have GCed class around " + i);
+ pass = 1;
+ i = 0;
+ }
+ }
+ }
+
+ if (gced < 0) {
+ logger.warn("unable to force GC");
+ //assertTrue(gced > 0);
+ }
+ }
+
+ static public void main(String[] args) throws Exception {
+ (new ClassCreatorTest()).runTest("testMany");
+ }
+}
Propchange: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl/ClassCreatorTest.java
------------------------------------------------------------------------------
svn:eol-style = native