# HG changeset patch # User redestad # Date 1491929866 -7200 # Node ID b9bf065070fe0f6dc78db4724119c2a8546d2fca # Parent 15a77e5b761261111226a9dee03d90c2f2745c10 8178387: Reduce memory churn when creating java.lang.invoke entities Reviewed-by: psandoz, vlivanov diff -r 15a77e5b7612 -r b9bf065070fe jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.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 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); } diff -r 15a77e5b7612 -r b9bf065070fe jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java --- 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; } diff -r 15a77e5b7612 -r b9bf065070fe jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java --- 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. diff -r 15a77e5b7612 -r b9bf065070fe jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java --- 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. diff -r 15a77e5b7612 -r b9bf065070fe jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java --- 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) { diff -r 15a77e5b7612 -r b9bf065070fe jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java --- 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); } /** diff -r 15a77e5b7612 -r b9bf065070fe jdk/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java --- 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; } diff -r 15a77e5b7612 -r b9bf065070fe jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java --- 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 Class forceType(Class type, Class 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) ||