]> source.dussan.org Git - javassist.git/commitdiff
implemented pruning
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Thu, 19 Aug 2004 14:04:32 +0000 (14:04 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Thu, 19 Aug 2004 14:04:32 +0000 (14:04 +0000)
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@125 30ef5769-5b8d-40dd-aea6-55b5d6557bb3

15 files changed:
src/main/javassist/ClassPool.java
src/main/javassist/CtBehavior.java
src/main/javassist/CtClass.java
src/main/javassist/CtClassType.java
src/main/javassist/CtNewClass.java
src/main/javassist/bytecode/AnnotationsAttribute.java
src/main/javassist/bytecode/ClassFile.java
src/main/javassist/bytecode/ConstPool.java
src/main/javassist/bytecode/FieldInfo.java
src/main/javassist/bytecode/MethodInfo.java
src/main/javassist/bytecode/ParameterAnnotationsAttribute.java
src/main/javassist/bytecode/annotation/AnnotationsWriter.java
src/main/javassist/bytecode/annotation/EnumMemberValue.java
src/main/javassist/compiler/MemberCodeGen.java
src/main/javassist/reflect/ClassMetaobject.java

index 6ddcf31da3d25f09d8a2f6429cd8fbeba567c09d..a75dec943bbf7788fe9bebacd4b4f27627515e0b 100644 (file)
@@ -37,6 +37,8 @@ import java.util.Hashtable;
  * of memory.  To avoid this, a <code>ClassPool</code> object
  * should be recreated, for example, every hundred classes processed.
  * Note that <code>getDefault()</code> is a singleton factory.
+ * Otherwise, <code>detach()</code> in <code>CtClass</code> should be used
+ * to avoid huge memory consumption.
  *
  * <p><b><code>ClassPool</code> hierarchy:</b>
  *
@@ -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;
+        }
     }
 
     /**
index 0ed161de2c42cb6dcec76aee49ce10fce0697335..3eac3eaf052a4a59786f8d9fcc3f02f92cc58bb7 100644 (file)
@@ -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();
 
index f4464c14b0278bd37fa99b3224a4978625457c48..fbb3569aa627571d837ba34042c3460bb2ee3480 100644 (file)
@@ -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,
+     * <p>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.
      *
+     * <p>If <code>defrost()</code> 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 <code>CtClass</code> object is converted into a class file.
+     * Pruning saves memory space since a <code>ClassPool</code> holds
+     * all instances of <code>CtClass</code>
+     * 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, <code>toBytecode()</code>,
+     * <code>writeFile()</code>, or <code>toClass()</code> cannot
+     * be called again.
+     *
+     * <p>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 <code>true</code> 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 <code>CtClass</code> from the <code>ClassPool</code>.
+     * Removes this <code>CtClass</code> object from the
+     * <code>ClassPool</code>.
      * After this method is called, any method cannot be called on the
      * removed <code>CtClass</code> object.
      *
-     * <p>If needed,
+     * <p>If <code>get()</code> in <code>ClassPool</code> is called
+     * with the name of the removed method,
      * the <code>ClassPool</code> will read the class file again
      * and constructs another <code>CtClass</code> object representing
      * the same class.
index 5ca4b69fd222c5a8eca79df6050021f3ec53db34..fa60df732db72d79ed7b0bd2c71535df1f41f64f 100644 (file)
@@ -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
     {
index bfc513b8042edf45beb34e51b67544d03dd12a6c..a1474cc100f40ed2a3fdbf056924a43c3983756c 100644 (file)
@@ -61,6 +61,7 @@ class CtNewClass extends CtClassType {
         if (!hasConstructor)
             try {
                 inheritAllConstructors();
+                hasConstructor = true;
             }
             catch (NotFoundException e) {
                 throw new CannotCompileException(e);
index 6a93f43a9ebb6bbd5202ac958bea83a33248101c..a6dfe87a917f6c959ac24d769789dae9031d26ec 100644 (file)
@@ -84,7 +84,7 @@ public class AnnotationsAttribute extends AttributeInfo {
      * @param cp            constant pool
      * @param attrname      attribute name (<code>visibleTag</code> or
      *                      <code>invisibleTag</code>).
-     * @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
      * <code>setAnnotations()</code>.
      *
-     * @see #setAnnotations()
+     * @see #setAnnotations(Annotation[])
      */
     public Annotation[] getAnnotations() {
         try {
index 79695296aa8e862c8febc504ac4d6a0ade9f3c1c..d9b6dfa6df99bece07426b5ddea591b6465a3f25 100644 (file)
@@ -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.
      */
index 6a024dbf85656d3385c022feedc733a522e90a0f..6f0b2bef071133654b179c7710e99759b980760a 100644 (file)
@@ -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.
      */
index 8cdb1ae3f65bf4b9f94a5fccc486bcd79c09c532..54ae1ba6211f3dec4bbc9bb2933e7d0f0091d469 100644 (file)
@@ -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 <code>field_info</code>.
index b3d3321380ff08cc4c08f965cf08a7242b056c9e..9e866578ff8a07f4ec76d7c1447a4a29956e1150 100644 (file)
@@ -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.
      */
index d44e4386e78388fb13a70df3776dcf650ffdcd8c..e4c6e23578fb47973c66cf53a9bd625c20797895 100644 (file)
@@ -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 {
index 155201c09816704e1baba55abf15e83e59369078..0d60950346f3846ed4f46ac29ab756c5b80af1aa 100644 (file)
@@ -23,7 +23,6 @@ import javassist.bytecode.ConstPool;
 /**
  * A convenience class for constructing a
  * <code>..Annotations_attribute</code>.
- * It is typically used together with <code>AnnotationsVisitor</code>.
  * See the source code of the <code>AnnotationsAttribute.Copier</code> class.
  *
  * <p>The following code snippet is an example of use of this class:
@@ -51,12 +50,11 @@ import javassist.bytecode.ConstPool;
  * corresponding to this annotation:
  *
  * <ul><pre>
- * @Author(name = "chiba", address = "tokyo")
+ * &nbsp;@Author(name = "chiba", address = "tokyo")
  * </pre></ul>
  *
- * @see AnnotationsAttribute
- * @see ParameterAnnotationsAttribute
- * @see AnnotationsVisitor
+ * @see javassist.bytecode.AnnotationsAttribute
+ * @see javassist.bytecode.ParameterAnnotationsAttribute
  */
 public class AnnotationsWriter {
     private OutputStream output;
index c059c24119c5aa9fedd1066944da48486ddf28d0..95c617adcd1302726352f28c7645fe98ff628a42 100644 (file)
@@ -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));
index 200a0033278e16740ebc5c48baca94872f431eae..a4e69f59c4c294e979de436967a15b0e510c6c91 100644 (file)
@@ -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());
     }
index 7a149e7d34da5d56b587d324748fd8dc7948a018..000846af940c237d100972a7c53576ae2844b653 100644 (file)
@@ -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.
      *
+     * <p>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