From: chiba Date: Thu, 19 Aug 2004 14:04:32 +0000 (+0000) Subject: implemented pruning X-Git-Tag: rel_3_17_1_ga~489 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=8c82477b4172ed0db9e5d866a0dba22ae20eb23f;p=javassist.git implemented pruning git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@125 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- diff --git a/src/main/javassist/ClassPool.java b/src/main/javassist/ClassPool.java index 6ddcf31d..a75dec94 100644 --- a/src/main/javassist/ClassPool.java +++ b/src/main/javassist/ClassPool.java @@ -37,6 +37,8 @@ import java.util.Hashtable; * of memory. To avoid this, a ClassPool object * should be recreated, for example, every hundred classes processed. * Note that getDefault() is a singleton factory. + * Otherwise, detach() in CtClass should be used + * to avoid huge memory consumption. * *

ClassPool hierarchy: * @@ -75,6 +77,8 @@ public class ClassPool { */ private Hashtable cflow = null; // should be synchronous. + private static final int INIT_HASH_SIZE = 191; + /** * Creates a root class pool. No parent class pool is specified. * @@ -91,7 +95,7 @@ public class ClassPool { * @see javassist.ClassPool#getDefault() */ public ClassPool(ClassPool parent) { - this.classes = new Hashtable(); + this.classes = new Hashtable(INIT_HASH_SIZE); this.source = new ClassPoolTail(); this.parent = parent; if (parent == null) { @@ -289,8 +293,10 @@ public class ClassPool { if (clazz == null) throw new NotFoundException(classname); - else + else { + clazz.incGetCounter(); return clazz; + } } /** diff --git a/src/main/javassist/CtBehavior.java b/src/main/javassist/CtBehavior.java index 0ed161de..3eac3eaf 100644 --- a/src/main/javassist/CtBehavior.java +++ b/src/main/javassist/CtBehavior.java @@ -355,7 +355,7 @@ public abstract class CtBehavior extends CtMember { throws CannotCompileException { // if the class is not frozen, - // does not trun the modified flag on. + // does not turn the modified flag on. if (declaringClass.isFrozen()) declaringClass.checkModify(); diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index f4464c14..fbb3569a 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -241,17 +241,48 @@ public abstract class CtClass { /** * Defrosts the class so that the class can be modified again. * - * To avoid changes that will be never reflected, + *

To avoid changes that will be never reflected, * the class is frozen to be unmodifiable if it is loaded or * written out. This method should be called only in a case * that the class will be reloaded or written out later again. * + *

If defrost() will be called later, pruning + * must be disallowed in advance. + * * @see #isFrozen() + * @see #stopPruning(boolean) */ public void defrost() { throw new RuntimeException("cannot defrost " + getName()); } + /** + * Disallows (or allows) pruning the data structure on memory + * when this CtClass object is converted into a class file. + * Pruning saves memory space since a ClassPool holds + * all instances of CtClass + * all the time of program execution. + * However, pruning discards the data representing part of the + * class definition, such as method bodies. + * Therefore, once it is pruned, toBytecode(), + * writeFile(), or toClass() cannot + * be called again. + * + *

Initially, pruning is allowed. + * + * @param stop disallow pruning if true. Otherwise, allow. + * @see #detach() + * @see #toBytecode() + * @see #toClass() + * @see #writeFile() + */ + public void stopPruning(boolean stop) {} + + /* Called by get() in ClassPool. + * CtClassType overrides this method. + */ + void incGetCounter() {} + /** * Returns true if this object represents a primitive * Java type: boolean, byte, char, short, int, long, float, double, @@ -904,11 +935,13 @@ public abstract class CtClass { } /** - * Removes this CtClass from the ClassPool. + * Removes this CtClass object from the + * ClassPool. * After this method is called, any method cannot be called on the * removed CtClass object. * - *

If needed, + *

If get() in ClassPool is called + * with the name of the removed method, * the ClassPool will read the class file again * and constructs another CtClass object representing * the same class. diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java index 5ca4b69f..fa60df73 100644 --- a/src/main/javassist/CtClassType.java +++ b/src/main/javassist/CtClassType.java @@ -29,6 +29,7 @@ import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; +import java.util.Enumeration; import java.util.List; /** @@ -37,7 +38,8 @@ import java.util.List; class CtClassType extends CtClass { ClassPool classPool; boolean wasChanged; - boolean wasFrozen; + private boolean wasFrozen; + boolean wasPruned; ClassFile classfile; private CtField fieldsCache; @@ -51,16 +53,22 @@ class CtClassType extends CtClass { private Hashtable hiddenMethods; // must be synchronous private int uniqueNumberSeed; + private boolean doPruning = true; + int getCounter; + private static int readCounter = 0; + private static final int READ_THRESHOLD = 100; // see getClassFile2() + CtClassType(String name, ClassPool cp) { super(name); classPool = cp; - wasChanged = wasFrozen = false; + wasChanged = wasFrozen = wasPruned = false; classfile = null; accessors = null; fieldInitializers = null; hiddenMethods = null; uniqueNumberSeed = 0; eraseCache(); + getCounter = 0; } CtClassType(InputStream ins, ClassPool cp) throws IOException { @@ -76,6 +84,9 @@ class CtClassType extends CtClass { if (wasFrozen) buffer.append("frozen "); + if (wasPruned) + buffer.append("pruned "); + buffer.append(Modifier.toString(getModifiers())); buffer.append(" class "); buffer.append(getName()); @@ -149,6 +160,11 @@ class CtClassType extends CtClass { if (classfile != null) return classfile; + if (readCounter++ > READ_THRESHOLD) { + doCompaction(); + readCounter = 0; + } + InputStream fin = null; try { fin = classPool.openClassfile(getName()); @@ -174,6 +190,28 @@ class CtClassType extends CtClass { } } + /* Inherited from CtClass. Called by get() in ClassPool. + * + * @see javassist.CtClass#incGetCounter() + */ + void incGetCounter() { ++getCounter; } + + private void doCompaction() { + Enumeration e = classPool.classes.elements(); + while (e.hasMoreElements()) { + Object obj = e.nextElement(); + if (obj instanceof CtClassType) { + CtClassType cct = (CtClassType)obj; + if (cct.getCounter < 2 && !cct.isModified()) { + cct.eraseCache(); + cct.classfile = null; + } + + cct.getCounter = 0; + } + } + } + public ClassPool getClassPool() { return classPool; } void setClassPool(ClassPool cp) { classPool = cp; } @@ -197,7 +235,10 @@ class CtClassType extends CtClass { wasChanged = true; } - public void defrost() { wasFrozen = false; } + public void defrost() { + checkPruned("defrost"); + wasFrozen = false; + } public boolean subtypeOf(CtClass clazz) throws NotFoundException { int i; @@ -855,15 +896,25 @@ class CtClassType extends CtClass { { try { if (isModified()) { + checkPruned("toBytecode"); ClassFile cf = getClassFile2(); modifyClassConstructor(cf); modifyConstructors(cf); cf.write(out); out.flush(); fieldInitializers = null; + if (doPruning) { + // to save memory + cf.prune(); + wasPruned = true; + } } - else + else { classPool.writeClassfile(getName(), out); + // to save memory + eraseCache(); + classfile = null; + } wasFrozen = true; } @@ -875,6 +926,16 @@ class CtClassType extends CtClass { } } + private void checkPruned(String method) { + if (wasPruned) + throw new RuntimeException(method + "(): " + getName() + + " was pruned."); + } + + public void stopPruning(boolean stop) { + doPruning = !stop; + } + private void modifyClassConstructor(ClassFile cf) throws CannotCompileException, NotFoundException { diff --git a/src/main/javassist/CtNewClass.java b/src/main/javassist/CtNewClass.java index bfc513b8..a1474cc1 100644 --- a/src/main/javassist/CtNewClass.java +++ b/src/main/javassist/CtNewClass.java @@ -61,6 +61,7 @@ class CtNewClass extends CtClassType { if (!hasConstructor) try { inheritAllConstructors(); + hasConstructor = true; } catch (NotFoundException e) { throw new CannotCompileException(e); diff --git a/src/main/javassist/bytecode/AnnotationsAttribute.java b/src/main/javassist/bytecode/AnnotationsAttribute.java index 6a93f43a..a6dfe87a 100644 --- a/src/main/javassist/bytecode/AnnotationsAttribute.java +++ b/src/main/javassist/bytecode/AnnotationsAttribute.java @@ -84,7 +84,7 @@ public class AnnotationsAttribute extends AttributeInfo { * @param cp constant pool * @param attrname attribute name (visibleTag or * invisibleTag). - * @see #setAnnotations(Annotations[]) + * @see #setAnnotations(Annotation[]) */ public AnnotationsAttribute(ConstPool cp, String attrname) { this(cp, attrname, new byte[] { 0, 0 }); @@ -127,7 +127,7 @@ public class AnnotationsAttribute extends AttributeInfo { * this object unless the tree is copied back to this object by * setAnnotations(). * - * @see #setAnnotations() + * @see #setAnnotations(Annotation[]) */ public Annotation[] getAnnotations() { try { diff --git a/src/main/javassist/bytecode/ClassFile.java b/src/main/javassist/bytecode/ClassFile.java index 79695296..d9b6dfa6 100644 --- a/src/main/javassist/bytecode/ClassFile.java +++ b/src/main/javassist/bytecode/ClassFile.java @@ -98,6 +98,42 @@ public final class ClassFile { return qname + ".java"; } + /** + * Discards all attributes, associated with both the class file and + * the members such as a code attribute and exceptions attribute. + * The unused constant pool entries are also discarded (a new packed + * constant pool is constructed). + */ + public void prune() { + ConstPool cp = new ConstPool(thisclassname); + superClass = cp.addClassInfo(getSuperclass()); + + if (interfaces != null) { + int n = interfaces.length; + for (int i = 0; i < n; ++i) + interfaces[i] + = cp.addClassInfo(constPool.getClassInfo(interfaces[i])); + } + + ArrayList list = methods; + int n = list.size(); + for (int i = 0; i < n; ++i) { + MethodInfo minfo = (MethodInfo)list.get(i); + minfo.prune(cp); + } + + list = fields; + n = list.size(); + for (int i = 0; i < n; ++i) { + FieldInfo finfo = (FieldInfo)list.get(i); + finfo.prune(cp); + } + + attributes = new LinkedList(); + cp.prune(); + constPool = cp; + } + /** * Returns a constant pool table. */ diff --git a/src/main/javassist/bytecode/ConstPool.java b/src/main/javassist/bytecode/ConstPool.java index 6a024dbf..6f0b2bef 100644 --- a/src/main/javassist/bytecode/ConstPool.java +++ b/src/main/javassist/bytecode/ConstPool.java @@ -124,6 +124,11 @@ public final class ConstPool { read(in); } + void prune() { + classes = new HashMap(); + strings = new HashMap(); + } + /** * Returns the name of the class using this constant pool table. */ diff --git a/src/main/javassist/bytecode/FieldInfo.java b/src/main/javassist/bytecode/FieldInfo.java index 8cdb1ae3..54ae1ba6 100644 --- a/src/main/javassist/bytecode/FieldInfo.java +++ b/src/main/javassist/bytecode/FieldInfo.java @@ -59,6 +59,13 @@ public final class FieldInfo { read(in); } + void prune(ConstPool cp) { + attribute = null; + name = cp.addUtf8Info(getName()); + descriptor = cp.addUtf8Info(getDescriptor()); + constPool = cp; + } + /** * Returns the constant pool table used * by this field_info. diff --git a/src/main/javassist/bytecode/MethodInfo.java b/src/main/javassist/bytecode/MethodInfo.java index b3d33213..9e866578 100644 --- a/src/main/javassist/bytecode/MethodInfo.java +++ b/src/main/javassist/bytecode/MethodInfo.java @@ -95,6 +95,13 @@ public final class MethodInfo { read(src, methodname, classnameMap); } + void prune(ConstPool cp) { + attribute = null; + name = cp.addUtf8Info(getName()); + descriptor = cp.addUtf8Info(getDescriptor()); + constPool = cp; + } + /** * Returns a method name. */ diff --git a/src/main/javassist/bytecode/ParameterAnnotationsAttribute.java b/src/main/javassist/bytecode/ParameterAnnotationsAttribute.java index d44e4386..e4c6e235 100644 --- a/src/main/javassist/bytecode/ParameterAnnotationsAttribute.java +++ b/src/main/javassist/bytecode/ParameterAnnotationsAttribute.java @@ -123,7 +123,7 @@ public class ParameterAnnotationsAttribute extends AttributeInfo { * @return Each element of the returned array represents an array of * annotations that are associated with each method parameter. * - * @see #setAnnotations() + * @see #setAnnotations(Annotation[][]) */ public Annotation[][] getAnnotations() { try { diff --git a/src/main/javassist/bytecode/annotation/AnnotationsWriter.java b/src/main/javassist/bytecode/annotation/AnnotationsWriter.java index 155201c0..0d609503 100644 --- a/src/main/javassist/bytecode/annotation/AnnotationsWriter.java +++ b/src/main/javassist/bytecode/annotation/AnnotationsWriter.java @@ -23,7 +23,6 @@ import javassist.bytecode.ConstPool; /** * A convenience class for constructing a * ..Annotations_attribute. - * It is typically used together with AnnotationsVisitor. * See the source code of the AnnotationsAttribute.Copier class. * *

The following code snippet is an example of use of this class: @@ -51,12 +50,11 @@ import javassist.bytecode.ConstPool; * corresponding to this annotation: * *

* - * @see AnnotationsAttribute - * @see ParameterAnnotationsAttribute - * @see AnnotationsVisitor + * @see javassist.bytecode.AnnotationsAttribute + * @see javassist.bytecode.ParameterAnnotationsAttribute */ public class AnnotationsWriter { private OutputStream output; diff --git a/src/main/javassist/bytecode/annotation/EnumMemberValue.java b/src/main/javassist/bytecode/annotation/EnumMemberValue.java index c059c241..95c617ad 100644 --- a/src/main/javassist/bytecode/annotation/EnumMemberValue.java +++ b/src/main/javassist/bytecode/annotation/EnumMemberValue.java @@ -64,7 +64,7 @@ public class EnumMemberValue extends MemberValue { /** * Changes the enum type name. * - * @param classname a fully-qualified type name. + * @param typename a fully-qualified type name. */ public void setType(String typename) { typeIndex = cp.addUtf8Info(Descriptor.of(typename)); diff --git a/src/main/javassist/compiler/MemberCodeGen.java b/src/main/javassist/compiler/MemberCodeGen.java index 200a0033..a4e69f59 100644 --- a/src/main/javassist/compiler/MemberCodeGen.java +++ b/src/main/javassist/compiler/MemberCodeGen.java @@ -464,7 +464,7 @@ public class MemberCodeGen extends CodeGen { if (maker != null) return maker.getConstructor(declClass, desc, minfo); } - + throw new CompileError("the called constructor is private in " + declClass.getName()); } diff --git a/src/main/javassist/reflect/ClassMetaobject.java b/src/main/javassist/reflect/ClassMetaobject.java index 7a149e7d..000846af 100644 --- a/src/main/javassist/reflect/ClassMetaobject.java +++ b/src/main/javassist/reflect/ClassMetaobject.java @@ -332,14 +332,14 @@ public class ClassMetaobject implements Serializable { * to the original method in the reflected class (i.e. not the proxy * method), using the original name of the method. * + *

Written by Brett Randall and Shigeru Chiba. + * * @param originalName The original name of the reflected method * @param argTypes array of Class specifying the method signature * @return the identifier index of the original method * @throws NoSuchMethodException if the method does not exist * * @see ClassMetaobject#getMethod(int) - * @author Brett Randall - * @author Shigeru Chiba */ public final int getMethodIndex(String originalName, Class[] argTypes) throws NoSuchMethodException