8178387: Reduce memory churn when creating java.lang.invoke entities
authorredestad
Tue, 11 Apr 2017 18:57:46 +0200
changeset 44591 b9bf065070fe
parent 44590 15a77e5b7612
child 44592 6b028630b652
8178387: Reduce memory churn when creating java.lang.invoke entities Reviewed-by: psandoz, vlivanov
jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java
jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java
jdk/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java
jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java	Tue Apr 11 11:24:12 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java	Tue Apr 11 18:57:46 2017 +0200
@@ -827,15 +827,27 @@
 
         private static String makeSignature(String types, boolean ctor) {
             StringBuilder buf = new StringBuilder(SIG_INCIPIT);
-            for (char c : types.toCharArray()) {
-                buf.append(typeSig(c));
+            int len = types.length();
+            for (int i = 0; i < len; i++) {
+                buf.append(typeSig(types.charAt(i)));
             }
             return buf.append(')').append(ctor ? "V" : BMH_SIG).toString();
         }
 
+        private static MethodType makeConstructorType(String types) {
+            int length = types.length();
+            Class<?> ptypes[] = new Class<?>[length + 2];
+            ptypes[0] = MethodType.class;
+            ptypes[1] = LambdaForm.class;
+            for (int i = 0; i < length; i++) {
+                ptypes[i + 2] = BasicType.basicType(types.charAt(i)).basicTypeClass();
+            }
+            return MethodType.makeImpl(BoundMethodHandle.class, ptypes, true);
+        }
+
         static MethodHandle makeCbmhCtor(Class<? extends BoundMethodHandle> cbmh, String types) {
             try {
-                return LOOKUP.findStatic(cbmh, "make", MethodType.fromDescriptor(makeSignature(types, false), null));
+                return LOOKUP.findStatic(cbmh, "make", makeConstructorType(types));
             } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | TypeNotPresentException e) {
                 throw newInternalError(e);
             }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Tue Apr 11 11:24:12 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Tue Apr 11 18:57:46 2017 +0200
@@ -73,6 +73,7 @@
     private static final String LL_SIG  = "(L" + OBJ + ";)L" + OBJ + ";";
     private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V";
     private static final String CLASS_PREFIX = LF + "$";
+    private static final String SOURCE_PREFIX = "LambdaForm$";
 
     /** Name of its super class*/
     static final String INVOKER_SUPER_NAME = OBJ;
@@ -80,9 +81,6 @@
     /** Name of new class */
     private final String className;
 
-    /** Name of the source file (for stack trace printing). */
-    private final String sourceFile;
-
     private final LambdaForm lambdaForm;
     private final String     invokerName;
     private final MethodType invokerType;
@@ -109,8 +107,7 @@
         if (DUMP_CLASS_FILES) {
             className = makeDumpableClassName(className);
         }
-        this.className  = CLASS_PREFIX + className;
-        this.sourceFile = "LambdaForm$" + className;
+        this.className  = className;
         this.lambdaForm = lambdaForm;
         this.invokerName = invokerName;
         this.invokerType = invokerType;
@@ -173,6 +170,13 @@
         }
     }
 
+    private void maybeDump(final byte[] classFile) {
+        if (DUMP_CLASS_FILES) {
+            maybeDump(CLASS_PREFIX + className, classFile);
+        }
+    }
+
+    // Also used from BoundMethodHandle
     static void maybeDump(final String className, final byte[] classFile) {
         if (DUMP_CLASS_FILES) {
             java.security.AccessController.doPrivileged(
@@ -306,8 +310,9 @@
     private ClassWriter classFilePrologue() {
         final int NOT_ACC_PUBLIC = 0;  // not ACC_PUBLIC
         cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
-        cw.visit(Opcodes.V1_8, NOT_ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, INVOKER_SUPER_NAME, null);
-        cw.visitSource(sourceFile, null);
+        cw.visit(Opcodes.V1_8, NOT_ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER,
+                CLASS_PREFIX + className, null, INVOKER_SUPER_NAME, null);
+        cw.visitSource(SOURCE_PREFIX + className, null);
         return cw;
     }
 
@@ -617,12 +622,11 @@
         return resolvedMember;
     }
 
-    private static MemberName lookupPregenerated(LambdaForm form) {
+    private static MemberName lookupPregenerated(LambdaForm form, MethodType invokerType) {
         if (form.customized != null) {
             // No pre-generated version for customized LF
             return null;
         }
-        MethodType invokerType = form.methodType();
         String name = form.kind.methodName;
         switch (form.kind) {
             case BOUND_REINVOKER: {
@@ -669,8 +673,10 @@
     /**
      * Generate customized bytecode for a given LambdaForm.
      */
-    static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
-        MemberName pregenerated = lookupPregenerated(form);
+    static MemberName generateCustomizedCode(LambdaForm form) {
+        final MethodType invokerType = form.methodType();
+
+        MemberName pregenerated = lookupPregenerated(form, invokerType);
         if (pregenerated != null)  return pregenerated; // pre-generated bytecode
 
         InvokerBytecodeGenerator g = new InvokerBytecodeGenerator("MH", form, invokerType);
@@ -720,7 +726,7 @@
         bogusMethod(lambdaForm);
 
         final byte[] classFile = toByteArray();
-        maybeDump(className, classFile);
+        maybeDump(classFile);
         return classFile;
     }
 
@@ -1761,7 +1767,7 @@
         bogusMethod(invokerType);
 
         final byte[] classFile = cw.toByteArray();
-        maybeDump(className, classFile);
+        maybeDump(classFile);
         return classFile;
     }
 
@@ -1829,7 +1835,7 @@
         bogusMethod(dstType);
 
         final byte[] classFile = cw.toByteArray();
-        maybeDump(className, classFile);
+        maybeDump(classFile);
         return classFile;
     }
 
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Tue Apr 11 11:24:12 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Tue Apr 11 18:57:46 2017 +0200
@@ -641,7 +641,7 @@
         for (int i = 0; i < arity; ++i) {
             ptypes[i] = parameterType(i).btClass;
         }
-        return MethodType.methodType(returnType().btClass, ptypes);
+        return MethodType.makeImpl(returnType().btClass, ptypes, true);
     }
 
     /** Return ABC_Z, where the ABC are parameter type characters, and Z is the return type character. */
@@ -677,7 +677,7 @@
         for (int i = 0; i < ptypes.length; i++)
             ptypes[i] = basicType(sig.charAt(i)).btClass;
         Class<?> rtype = signatureReturn(sig).btClass;
-        return MethodType.methodType(rtype, ptypes);
+        return MethodType.makeImpl(rtype, ptypes, true);
     }
 
     /**
@@ -847,10 +847,9 @@
         if (vmentry != null && isCompiled) {
             return;  // already compiled somehow
         }
-        MethodType invokerType = methodType();
-        assert(vmentry == null || vmentry.getMethodType().basicType().equals(invokerType));
+        assert(vmentry == null || vmentry.getMethodType().basicType().equals(methodType()));
         try {
-            vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this, invokerType);
+            vmentry = InvokerBytecodeGenerator.generateCustomizedCode(this);
             if (TRACE_INTERPRETER)
                 traceInterpreter("compileToBytecode", this);
             isCompiled = true;
@@ -901,10 +900,6 @@
         }
         return true;
     }
-    private static boolean returnTypesMatch(String sig, Object[] av, Object res) {
-        MethodHandle mh = (MethodHandle) av[0];
-        return valueMatches(signatureReturn(sig), mh.type().returnType(), res);
-    }
     private static boolean checkInt(Class<?> type, Object x) {
         assert(x instanceof Integer);
         if (type == int.class)  return true;
@@ -1179,7 +1174,6 @@
             // If we have a cached invoker, call it right away.
             // NOTE: The invoker always returns a reference value.
             if (TRACE_INTERPRETER)  return invokeWithArgumentsTracing(arguments);
-            assert(checkArgumentTypes(arguments, methodType()));
             return invoker().invokeBasic(resolvedHandle(), arguments);
         }
 
@@ -1197,7 +1191,6 @@
                     traceInterpreter("| resolve", this);
                     resolvedHandle();
                 }
-                assert(checkArgumentTypes(arguments, methodType()));
                 rval = invoker().invokeBasic(resolvedHandle(), arguments);
             } catch (Throwable ex) {
                 traceInterpreter("] throw =>", ex);
@@ -1213,23 +1206,6 @@
             return invoker = computeInvoker(methodType().form());
         }
 
-        private static boolean checkArgumentTypes(Object[] arguments, MethodType methodType) {
-            if (true)  return true;  // FIXME
-            MethodType dstType = methodType.form().erasedType();
-            MethodType srcType = dstType.basicType().wrap();
-            Class<?>[] ptypes = new Class<?>[arguments.length];
-            for (int i = 0; i < arguments.length; i++) {
-                Object arg = arguments[i];
-                Class<?> ptype = arg == null ? Object.class : arg.getClass();
-                // If the dest. type is a primitive we keep the
-                // argument type.
-                ptypes[i] = dstType.parameterType(i).isPrimitive() ? ptype : Object.class;
-            }
-            MethodType argType = MethodType.methodType(srcType.returnType(), ptypes).wrap();
-            assert(argType.isConvertibleTo(srcType)) : "wrong argument types: cannot convert " + argType + " to " + srcType;
-            return true;
-        }
-
         MethodType methodType() {
             if (resolvedHandle != null)
                 return resolvedHandle.type();
@@ -1725,7 +1701,7 @@
         boolean isVoid = (type == V_TYPE);
         Class<?> btClass = type.btClass;
         MethodType zeType = MethodType.methodType(btClass);
-        MethodType idType = (isVoid) ? zeType : zeType.appendParameterTypes(btClass);
+        MethodType idType = (isVoid) ? zeType : MethodType.methodType(btClass, btClass);
 
         // Look up symbolic names.  It might not be necessary to have these,
         // but if we need to emit direct references to bytecodes, it helps.
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java	Tue Apr 11 11:24:12 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java	Tue Apr 11 18:57:46 2017 +0200
@@ -149,7 +149,7 @@
                 Object[] typeInfo = (Object[]) type;
                 Class<?>[] ptypes = (Class<?>[]) typeInfo[1];
                 Class<?> rtype = (Class<?>) typeInfo[0];
-                MethodType res = MethodType.methodType(rtype, ptypes);
+                MethodType res = MethodType.makeImpl(rtype, ptypes, true);
                 type = res;
             }
             // Make sure type is a MethodType for racing threads.
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Tue Apr 11 11:24:12 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Tue Apr 11 18:57:46 2017 +0200
@@ -1674,6 +1674,7 @@
 
     // Local constant functions:
 
+    /* non-public */
     static final byte NF_checkSpreadArgument = 0,
             NF_guardWithCatch = 1,
             NF_throwException = 2,
@@ -1682,7 +1683,6 @@
             NF_profileBoolean = 5,
             NF_LIMIT = 6;
 
-    /*non-public*/
     private static final @Stable NamedFunction[] NFS = new NamedFunction[NF_LIMIT];
 
     static NamedFunction getFunction(byte func) {
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java	Tue Apr 11 11:24:12 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java	Tue Apr 11 18:57:46 2017 +0200
@@ -95,7 +95,7 @@
     private static final long serialVersionUID = 292L;  // {rtype, {ptype...}}
 
     // The rtype and ptypes fields define the structural identity of the method type:
-    private final Class<?>   rtype;
+    private final @Stable Class<?>   rtype;
     private final @Stable Class<?>[] ptypes;
 
     // The remaining fields are caches of various sorts:
@@ -117,7 +117,8 @@
 
     /**
      * Construct a temporary unchecked instance of MethodType for use only as a key to the intern table.
-     * Does not check the given parameters for validity, and must be discarded after it is used as a searching key.
+     * Does not check the given parameters for validity, and must discarded (if untrusted) or checked
+     * (if trusted) after it has been used as a searching key.
      * The parameters are reversed for this constructor, so that it is not accidentally used.
      */
     private MethodType(Class<?>[] ptypes, Class<?> rtype) {
@@ -181,6 +182,7 @@
         checkSlotCount(ptypes.length + slots);
         return slots;
     }
+
     static {
         // MAX_JVM_ARITY must be power of 2 minus 1 for following code trick to work:
         assert((MAX_JVM_ARITY & (MAX_JVM_ARITY+1)) == 0);
@@ -303,18 +305,26 @@
      */
     /*trusted*/ static
     MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) {
-        MethodType mt = internTable.get(new MethodType(ptypes, rtype));
-        if (mt != null)
-            return mt;
         if (ptypes.length == 0) {
             ptypes = NO_PTYPES; trusted = true;
         }
-        mt = new MethodType(rtype, ptypes, trusted);
+        MethodType primordialMT = new MethodType(ptypes, rtype);
+        MethodType mt = internTable.get(primordialMT);
+        if (mt != null)
+            return mt;
+
         // promote the object to the Real Thing, and reprobe
+        if (trusted) {
+            MethodType.checkRtype(rtype);
+            MethodType.checkPtypes(ptypes);
+            mt = primordialMT;
+        } else {
+            mt = new MethodType(rtype, ptypes, false);
+        }
         mt.form = MethodTypeForm.findForm(mt);
         return internTable.add(mt);
     }
-    private static final MethodType[] objectOnlyTypes = new MethodType[20];
+    private static final @Stable MethodType[] objectOnlyTypes = new MethodType[20];
 
     /**
      * Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array.
@@ -398,9 +408,14 @@
         checkSlotCount(parameterSlotCount() + ptypesToInsert.length + ins);
         int ilen = ptypesToInsert.length;
         if (ilen == 0)  return this;
-        Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+ilen);
-        System.arraycopy(nptypes, num, nptypes, num+ilen, len-num);
+        Class<?>[] nptypes = new Class<?>[len + ilen];
+        if (num > 0) {
+            System.arraycopy(ptypes, 0, nptypes, 0, num);
+        }
         System.arraycopy(ptypesToInsert, 0, nptypes, num, ilen);
+        if (num < len) {
+            System.arraycopy(ptypes, num, nptypes, num+ilen, len-num);
+        }
         return makeImpl(rtype, nptypes, true);
     }
 
@@ -636,11 +651,14 @@
         return form.basicType();
     }
 
+    private static final @Stable Class<?>[] METHOD_HANDLE_ARRAY
+            = new Class<?>[] { MethodHandle.class };
+
     /**
      * @return a version of the original type with MethodHandle prepended as the first argument
      */
     /*non-public*/ MethodType invokerType() {
-        return insertParameterTypes(0, MethodHandle.class);
+        return insertParameterTypes(0, METHOD_HANDLE_ARRAY);
     }
 
     /**
--- a/jdk/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java	Tue Apr 11 11:24:12 2017 +0200
+++ b/jdk/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java	Tue Apr 11 18:57:46 2017 +0200
@@ -297,10 +297,13 @@
      * @param refc the class attempting to make the reference
      */
     public static boolean isTypeVisible(java.lang.invoke.MethodType type, Class<?> refc) {
-        for (int n = -1, max = type.parameterCount(); n < max; n++) {
-            Class<?> ptype = (n < 0 ? type.returnType() : type.parameterType(n));
-            if (!isTypeVisible(ptype, refc))
+        if (!isTypeVisible(type.returnType(), refc)) {
+            return false;
+        }
+        for (int n = 0, max = type.parameterCount(); n < max; n++) {
+            if (!isTypeVisible(type.parameterType(n), refc)) {
                 return false;
+            }
         }
         return true;
     }
--- a/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java	Tue Apr 11 11:24:12 2017 +0200
+++ b/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java	Tue Apr 11 18:57:46 2017 +0200
@@ -518,12 +518,6 @@
      * If the target type is a primitive, change it to a wrapper.
      */
     static <T> Class<T> forceType(Class<?> type, Class<T> exampleType) {
-        boolean z = (type == exampleType ||
-               type.isPrimitive() && forPrimitiveType(type) == findWrapperType(exampleType) ||
-               exampleType.isPrimitive() && forPrimitiveType(exampleType) == findWrapperType(type) ||
-               type == Object.class && !exampleType.isPrimitive());
-        if (!z)
-            System.out.println(type+" <= "+exampleType);
         assert(type == exampleType ||
                type.isPrimitive() && forPrimitiveType(type) == findWrapperType(exampleType) ||
                exampleType.isPrimitive() && forPrimitiveType(exampleType) == findWrapperType(type) ||