8058100: Reduce the RecompilableScriptFunctionData footprint

Thu, 11 Sep 2014 17:12:38 +0200

author
attila
date
Thu, 11 Sep 2014 17:12:38 +0200
changeset 1005
2cad9bf911a4
parent 1004
698280da463a
child 1006
e94bfa3c6c6c

8058100: Reduce the RecompilableScriptFunctionData footprint
Reviewed-by: jlaskey, lagergren

src/jdk/nashorn/internal/codegen/Compiler.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/FindScopeDepths.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/AllocationStrategy.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/jdk/nashorn/internal/codegen/Compiler.java	Wed Sep 10 12:37:44 2014 +0200
     1.2 +++ b/src/jdk/nashorn/internal/codegen/Compiler.java	Thu Sep 11 17:12:38 2014 +0200
     1.3 @@ -736,13 +736,6 @@
     1.4          return name.replace('/', '.');
     1.5      }
     1.6  
     1.7 -    RecompilableScriptFunctionData getProgram() {
     1.8 -        if (compiledFunction == null) {
     1.9 -            return null;
    1.10 -        }
    1.11 -        return compiledFunction.getProgram();
    1.12 -    }
    1.13 -
    1.14      RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
    1.15          return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
    1.16      }
     2.1 --- a/src/jdk/nashorn/internal/codegen/FindScopeDepths.java	Wed Sep 10 12:37:44 2014 +0200
     2.2 +++ b/src/jdk/nashorn/internal/codegen/FindScopeDepths.java	Thu Sep 11 17:12:38 2014 +0200
     2.3 @@ -25,8 +25,6 @@
     2.4  
     2.5  package jdk.nashorn.internal.codegen;
     2.6  
     2.7 -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getClassName;
     2.8 -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getPaddedFieldCount;
     2.9  import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
    2.10  
    2.11  import java.util.HashMap;
    2.12 @@ -34,6 +32,7 @@
    2.13  import java.util.Iterator;
    2.14  import java.util.Map;
    2.15  import java.util.Set;
    2.16 +import jdk.nashorn.internal.codegen.ObjectClassGenerator.AllocatorDescriptor;
    2.17  import jdk.nashorn.internal.ir.Block;
    2.18  import jdk.nashorn.internal.ir.FunctionNode;
    2.19  import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
    2.20 @@ -44,7 +43,6 @@
    2.21  import jdk.nashorn.internal.ir.WithNode;
    2.22  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    2.23  import jdk.nashorn.internal.runtime.Context;
    2.24 -import jdk.nashorn.internal.runtime.PropertyMap;
    2.25  import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
    2.26  import jdk.nashorn.internal.runtime.logging.DebugLogger;
    2.27  import jdk.nashorn.internal.runtime.logging.Loggable;
    2.28 @@ -208,14 +206,10 @@
    2.29  
    2.30          assert nestedFunctions != null;
    2.31          // Generate the object class and property map in case this function is ever used as constructor
    2.32 -        final int         fieldCount         = getPaddedFieldCount(newFunctionNode.getThisProperties());
    2.33 -        final String      allocatorClassName = Compiler.binaryName(getClassName(fieldCount));
    2.34 -        final PropertyMap allocatorMap       = PropertyMap.newMap(null, allocatorClassName, 0, fieldCount, 0);
    2.35          final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData(
    2.36                  newFunctionNode,
    2.37                  compiler.getCodeInstaller(),
    2.38 -                allocatorClassName,
    2.39 -                allocatorMap,
    2.40 +                new AllocatorDescriptor(newFunctionNode.getThisProperties()),
    2.41                  nestedFunctions,
    2.42                  externalSymbolDepths.get(fnId),
    2.43                  internalSymbols.get(fnId)
     3.1 --- a/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java	Wed Sep 10 12:37:44 2014 +0200
     3.2 +++ b/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java	Thu Sep 11 17:12:38 2014 +0200
     3.3 @@ -53,7 +53,6 @@
     3.4  import java.util.Iterator;
     3.5  import java.util.LinkedList;
     3.6  import java.util.List;
     3.7 -
     3.8  import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
     3.9  import jdk.nashorn.internal.codegen.types.Type;
    3.10  import jdk.nashorn.internal.runtime.AccessorProperty;
    3.11 @@ -826,5 +825,45 @@
    3.12          return MH.findStatic(MethodHandles.lookup(), ObjectClassGenerator.class, name, MH.type(rtype, types));
    3.13      }
    3.14  
    3.15 +    /**
    3.16 +     * Describes the allocator class name and property map for a constructor function with the specified
    3.17 +     * number of "this" properties that it initializes.
    3.18 +     *
    3.19 +     */
    3.20 +    public static class AllocatorDescriptor {
    3.21 +        private final String allocatorClassName;
    3.22 +        private final PropertyMap allocatorMap;
    3.23  
    3.24 +        /**
    3.25 +         * Creates a new allocator descriptor
    3.26 +         * @param thisProperties the number of "this" properties that the function initializes
    3.27 +         */
    3.28 +        public AllocatorDescriptor(final int thisProperties) {
    3.29 +            final int paddedFieldCount = getPaddedFieldCount(thisProperties);
    3.30 +            this.allocatorClassName = Compiler.binaryName(getClassName(paddedFieldCount));
    3.31 +            this.allocatorMap = PropertyMap.newMap(null, allocatorClassName, 0, paddedFieldCount, 0);
    3.32 +        }
    3.33 +
    3.34 +        /**
    3.35 +         * Returns the name of the class that the function allocates
    3.36 +         * @return the name of the class that the function allocates
    3.37 +         */
    3.38 +        public String getAllocatorClassName() {
    3.39 +            return allocatorClassName;
    3.40 +        }
    3.41 +
    3.42 +        /**
    3.43 +         * Returns the allocator map for the function.
    3.44 +         * @return the allocator map for the function.
    3.45 +         */
    3.46 +        public PropertyMap getAllocatorMap() {
    3.47 +            return allocatorMap;
    3.48 +        }
    3.49 +
    3.50 +        @Override
    3.51 +        public String toString() {
    3.52 +            return "AllocatorDescriptor[allocatorClassName=" + allocatorClassName + ", allocatorMap.size=" +
    3.53 +                    allocatorMap.size() + "]";
    3.54 +        }
    3.55 +    }
    3.56  }
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/src/jdk/nashorn/internal/runtime/AllocationStrategy.java	Thu Sep 11 17:12:38 2014 +0200
     4.3 @@ -0,0 +1,104 @@
     4.4 +/*
     4.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
     4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4.7 + *
     4.8 + * This code is free software; you can redistribute it and/or modify it
     4.9 + * under the terms of the GNU General Public License version 2 only, as
    4.10 + * published by the Free Software Foundation.  Oracle designates this
    4.11 + * particular file as subject to the "Classpath" exception as provided
    4.12 + * by Oracle in the LICENSE file that accompanied this code.
    4.13 + *
    4.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    4.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    4.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    4.17 + * version 2 for more details (a copy is included in the LICENSE file that
    4.18 + * accompanied this code).
    4.19 + *
    4.20 + * You should have received a copy of the GNU General Public License version
    4.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    4.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    4.23 + *
    4.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    4.25 + * or visit www.oracle.com if you need additional information or have any
    4.26 + * questions.
    4.27 + */
    4.28 +package jdk.nashorn.internal.runtime;
    4.29 +
    4.30 +import static jdk.nashorn.internal.lookup.Lookup.MH;
    4.31 +
    4.32 +import java.io.Serializable;
    4.33 +import java.lang.invoke.MethodHandle;
    4.34 +import java.lang.invoke.MethodHandles;
    4.35 +import jdk.nashorn.internal.codegen.CompilerConstants;
    4.36 +import jdk.nashorn.internal.codegen.ObjectClassGenerator.AllocatorDescriptor;
    4.37 +
    4.38 +/**
    4.39 + * Encapsulates the allocation strategy for a function when used as a constructor. Basically the same as
    4.40 + * {@link AllocatorDescriptor}, but with an additionally cached resolved method handle. There is also a
    4.41 + * canonical default allocation strategy for functions that don't assign any "this" properties (vast majority
    4.42 + * of all functions), therefore saving some storage space in {@link RecompilableScriptFunctionData} that would
    4.43 + * otherwise be lost to identical tuples of (map, className, handle) fields.
    4.44 + */
    4.45 +final class AllocationStrategy implements Serializable {
    4.46 +    private static final long serialVersionUID = 1L;
    4.47 +
    4.48 +    private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
    4.49 +
    4.50 +    private static final AllocationStrategy DEFAULT_STRATEGY = new AllocationStrategy(new AllocatorDescriptor(0));
    4.51 +
    4.52 +    /** Allocator map from allocator descriptor */
    4.53 +    private final PropertyMap allocatorMap;
    4.54 +
    4.55 +    /** Name of class where allocator function resides */
    4.56 +    private final String allocatorClassName;
    4.57 +
    4.58 +    /** lazily generated allocator */
    4.59 +    private transient MethodHandle allocator;
    4.60 +
    4.61 +    private AllocationStrategy(final AllocatorDescriptor desc) {
    4.62 +        this.allocatorMap = desc.getAllocatorMap();
    4.63 +        // These classes get loaded, so an interned variant of their name is most likely around anyway.
    4.64 +        this.allocatorClassName = desc.getAllocatorClassName().intern();
    4.65 +    }
    4.66 +
    4.67 +    private boolean matches(final AllocatorDescriptor desc) {
    4.68 +        return desc.getAllocatorMap().size() == allocatorMap.size() &&
    4.69 +                desc.getAllocatorClassName().equals(allocatorClassName);
    4.70 +    }
    4.71 +
    4.72 +    static AllocationStrategy get(final AllocatorDescriptor desc) {
    4.73 +        return DEFAULT_STRATEGY.matches(desc) ? DEFAULT_STRATEGY : new AllocationStrategy(desc);
    4.74 +    }
    4.75 +
    4.76 +    PropertyMap getAllocatorMap() {
    4.77 +        return allocatorMap;
    4.78 +    }
    4.79 +
    4.80 +    ScriptObject allocate(final PropertyMap map) {
    4.81 +        try {
    4.82 +            if (allocator == null) {
    4.83 +                allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName),
    4.84 +                        CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
    4.85 +            }
    4.86 +            return (ScriptObject)allocator.invokeExact(map);
    4.87 +        } catch (final RuntimeException | Error e) {
    4.88 +            throw e;
    4.89 +        } catch (final Throwable t) {
    4.90 +            throw new RuntimeException(t);
    4.91 +        }
    4.92 +    }
    4.93 +
    4.94 +    private Object readResolve() {
    4.95 +        if(allocatorMap.size() == DEFAULT_STRATEGY.allocatorMap.size() &&
    4.96 +                allocatorClassName.equals(DEFAULT_STRATEGY.allocatorClassName)) {
    4.97 +            return DEFAULT_STRATEGY;
    4.98 +        }
    4.99 +        return this;
   4.100 +    }
   4.101 +
   4.102 +    @Override
   4.103 +    public String toString() {
   4.104 +        return "AllocationStrategy[allocatorClassName=" + allocatorClassName + ", allocatorMap.size=" +
   4.105 +                allocatorMap.size() + "]";
   4.106 +    }
   4.107 +}
     5.1 --- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Wed Sep 10 12:37:44 2014 +0200
     5.2 +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Thu Sep 11 17:12:38 2014 +0200
     5.3 @@ -42,6 +42,7 @@
     5.4  import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
     5.5  import jdk.nashorn.internal.codegen.CompilerConstants;
     5.6  import jdk.nashorn.internal.codegen.FunctionSignature;
     5.7 +import jdk.nashorn.internal.codegen.ObjectClassGenerator.AllocatorDescriptor;
     5.8  import jdk.nashorn.internal.codegen.OptimisticTypesPersistence;
     5.9  import jdk.nashorn.internal.codegen.TypeMap;
    5.10  import jdk.nashorn.internal.codegen.types.Type;
    5.11 @@ -55,7 +56,6 @@
    5.12  import jdk.nashorn.internal.runtime.logging.DebugLogger;
    5.13  import jdk.nashorn.internal.runtime.logging.Loggable;
    5.14  import jdk.nashorn.internal.runtime.logging.Logger;
    5.15 -
    5.16  /**
    5.17   * This is a subclass that represents a script function that may be regenerated,
    5.18   * for example with specialization based on call site types, or lazily generated.
    5.19 @@ -81,8 +81,12 @@
    5.20      /** Token of this function within the source. */
    5.21      private final long token;
    5.22  
    5.23 -    /** Allocator map from makeMap() */
    5.24 -    private final PropertyMap allocatorMap;
    5.25 +    /**
    5.26 +     * Represents the allocation strategy (property map, script object class, and method handle) for when
    5.27 +     * this function is used as a constructor. Note that majority of functions (those not setting any this.*
    5.28 +     * properties) will share a single canonical "default strategy" instance.
    5.29 +     */
    5.30 +    private final AllocationStrategy allocationStrategy;
    5.31  
    5.32      /**
    5.33       * Opaque object representing parser state at the end of the function. Used when reparsing outer function
    5.34 @@ -93,12 +97,6 @@
    5.35      /** Code installer used for all further recompilation/specialization of this ScriptFunction */
    5.36      private transient CodeInstaller<ScriptEnvironment> installer;
    5.37  
    5.38 -    /** Name of class where allocator function resides */
    5.39 -    private final String allocatorClassName;
    5.40 -
    5.41 -    /** lazily generated allocator */
    5.42 -    private transient MethodHandle allocator;
    5.43 -
    5.44      private final Map<Integer, RecompilableScriptFunctionData> nestedFunctions;
    5.45  
    5.46      /** Id to parent function if one exists */
    5.47 @@ -124,8 +122,7 @@
    5.48       *
    5.49       * @param functionNode        functionNode that represents this function code
    5.50       * @param installer           installer for code regeneration versions of this function
    5.51 -     * @param allocatorClassName  name of our allocator class, will be looked up dynamically if used as a constructor
    5.52 -     * @param allocatorMap        allocator map to seed instances with, when constructing
    5.53 +     * @param allocationDescriptor descriptor for the allocation behavior when this function is used as a constructor
    5.54       * @param nestedFunctions     nested function map
    5.55       * @param externalScopeDepths external scope depths
    5.56       * @param internalSymbols     internal symbols to method, defined in its scope
    5.57 @@ -133,8 +130,7 @@
    5.58      public RecompilableScriptFunctionData(
    5.59          final FunctionNode functionNode,
    5.60          final CodeInstaller<ScriptEnvironment> installer,
    5.61 -        final String allocatorClassName,
    5.62 -        final PropertyMap allocatorMap,
    5.63 +        final AllocatorDescriptor allocationDescriptor,
    5.64          final Map<Integer, RecompilableScriptFunctionData> nestedFunctions,
    5.65          final Map<String, Integer> externalScopeDepths,
    5.66          final Set<String> internalSymbols) {
    5.67 @@ -151,11 +147,10 @@
    5.68          this.endParserState      = functionNode.getEndParserState();
    5.69          this.token               = tokenFor(functionNode);
    5.70          this.installer           = installer;
    5.71 -        this.allocatorClassName  = allocatorClassName;
    5.72 -        this.allocatorMap        = allocatorMap;
    5.73 -        this.nestedFunctions     = nestedFunctions;
    5.74 -        this.externalScopeDepths = externalScopeDepths;
    5.75 -        this.internalSymbols     = new HashSet<>(internalSymbols);
    5.76 +        this.allocationStrategy  = AllocationStrategy.get(allocationDescriptor);
    5.77 +        this.nestedFunctions     = smallMap(nestedFunctions);
    5.78 +        this.externalScopeDepths = smallMap(externalScopeDepths);
    5.79 +        this.internalSymbols     = smallSet(new HashSet<>(internalSymbols));
    5.80  
    5.81          for (final RecompilableScriptFunctionData nfn : nestedFunctions.values()) {
    5.82              assert nfn.getParent() == null;
    5.83 @@ -165,6 +160,27 @@
    5.84          createLogger();
    5.85      }
    5.86  
    5.87 +    private static <K, V> Map<K, V> smallMap(final Map<K, V> map) {
    5.88 +        if (map == null || map.isEmpty()) {
    5.89 +            return Collections.emptyMap();
    5.90 +        } else if (map.size() == 1) {
    5.91 +            final Map.Entry<K, V> entry = map.entrySet().iterator().next();
    5.92 +            return Collections.singletonMap(entry.getKey(), entry.getValue());
    5.93 +        } else {
    5.94 +            return map;
    5.95 +        }
    5.96 +    }
    5.97 +
    5.98 +    private static <T> Set<T> smallSet(final Set<T> set) {
    5.99 +        if (set == null || set.isEmpty()) {
   5.100 +            return Collections.emptySet();
   5.101 +        } else if (set.size() == 1) {
   5.102 +            return Collections.singleton(set.iterator().next());
   5.103 +        } else {
   5.104 +            return set;
   5.105 +        }
   5.106 +    }
   5.107 +
   5.108      @Override
   5.109      public DebugLogger getLogger() {
   5.110          return log;
   5.111 @@ -194,11 +210,7 @@
   5.112       * @return the external symbol table with proto depths
   5.113       */
   5.114      public int getExternalSymbolDepth(final String symbolName) {
   5.115 -        final Map<String, Integer> map = externalScopeDepths;
   5.116 -        if (map == null) {
   5.117 -            return -1;
   5.118 -        }
   5.119 -        final Integer depth = map.get(symbolName);
   5.120 +        final Integer depth = externalScopeDepths.get(symbolName);
   5.121          if (depth == null) {
   5.122              return -1;
   5.123          }
   5.124 @@ -210,8 +222,7 @@
   5.125       * @return the names of all external symbols this function uses.
   5.126       */
   5.127      public Set<String> getExternalSymbolNames() {
   5.128 -        return externalScopeDepths == null ? Collections.<String>emptySet() :
   5.129 -            Collections.unmodifiableSet(externalScopeDepths.keySet());
   5.130 +        return Collections.unmodifiableSet(externalScopeDepths.keySet());
   5.131      }
   5.132  
   5.133      /**
   5.134 @@ -334,25 +345,12 @@
   5.135  
   5.136      @Override
   5.137      PropertyMap getAllocatorMap() {
   5.138 -        return allocatorMap;
   5.139 +        return allocationStrategy.getAllocatorMap();
   5.140      }
   5.141  
   5.142      @Override
   5.143      ScriptObject allocate(final PropertyMap map) {
   5.144 -        try {
   5.145 -            ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try
   5.146 -            return allocator == null ? null : (ScriptObject)allocator.invokeExact(map);
   5.147 -        } catch (final RuntimeException | Error e) {
   5.148 -            throw e;
   5.149 -        } catch (final Throwable t) {
   5.150 -            throw new RuntimeException(t);
   5.151 -        }
   5.152 -    }
   5.153 -
   5.154 -    private void ensureHasAllocator() throws ClassNotFoundException {
   5.155 -        if (allocator == null && allocatorClassName != null) {
   5.156 -            this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
   5.157 -        }
   5.158 +        return allocationStrategy.allocate(map);
   5.159      }
   5.160  
   5.161      FunctionNode reparse() {
   5.162 @@ -756,21 +754,6 @@
   5.163      }
   5.164  
   5.165      /**
   5.166 -     * Get the uppermost parent, the program, for this data
   5.167 -     * @return program
   5.168 -     */
   5.169 -    public RecompilableScriptFunctionData getProgram() {
   5.170 -        RecompilableScriptFunctionData program = this;
   5.171 -        while (true) {
   5.172 -            final RecompilableScriptFunctionData p = program.getParent();
   5.173 -            if (p == null) {
   5.174 -                return program;
   5.175 -            }
   5.176 -            program = p;
   5.177 -        }
   5.178 -    }
   5.179 -
   5.180 -    /**
   5.181       * Check whether a certain name is a global symbol, i.e. only exists as defined
   5.182       * in outermost scope and not shadowed by being parameter or assignment in inner
   5.183       * scopes

mercurial