8059346: Single class loader is used to load compiled bytecode
Reviewed-by: hannesw, lagergren
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CodeInstaller.java Wed Oct 01 12:22:12 2014 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CodeInstaller.java Thu Oct 02 16:30:49 2014 +0200
@@ -100,4 +100,21 @@
* @return compiled script data
*/
public StoredScript loadScript(Source source, String functionKey);
+
+ /**
+ * Returns a new code installer that shares most of the functionality of this code installer, but uses a
+ * new, independent class loader.
+ * @return a new code installer with a new independent class loader.
+ */
+ public CodeInstaller<T> withNewLoader();
+
+ /**
+ * Returns true if this code installer is compatible with the other code installer. Compatibility is expected to be
+ * an equivalence relation, and installers are supposed to be compatible with those they create using
+ * {@link #withNewLoader()}.
+ * @param other the other code installer tested for compatibility with this code installer.
+ * @return true if this code installer is compatible with the other code installer.
+ */
+ public boolean isCompatibleWith(CodeInstaller<T> other);
+
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java Wed Oct 01 12:22:12 2014 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java Thu Oct 02 16:30:49 2014 +0200
@@ -33,6 +33,7 @@
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
+
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
@@ -158,7 +159,7 @@
}
/**
- * Return the context for this installer
+ * Return the script environment for this installer
* @return ScriptEnvironment
*/
@Override
@@ -222,6 +223,20 @@
}
return null;
}
+
+ @Override
+ public CodeInstaller<ScriptEnvironment> withNewLoader() {
+ return new ContextCodeInstaller(context, context.createNewLoader(), codeSource);
+ }
+
+ @Override
+ public boolean isCompatibleWith(final CodeInstaller<ScriptEnvironment> other) {
+ if (other instanceof ContextCodeInstaller) {
+ final ContextCodeInstaller cci = (ContextCodeInstaller)other;
+ return cci.context == context && cci.codeSource == codeSource;
+ }
+ return false;
+ }
}
/** Is Context global debug mode enabled ? */
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Oct 01 12:22:12 2014 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Thu Oct 02 16:30:49 2014 +0200
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
+
import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@@ -268,7 +269,7 @@
if (this.source == null && this.installer == null) {
this.source = src;
this.installer = inst;
- } else if (this.source != src || this.installer != inst) {
+ } else if (this.source != src || !this.installer.isCompatibleWith(inst)) {
// Existing values must be same as those passed as parameters
throw new IllegalArgumentException();
}
@@ -407,6 +408,17 @@
return getCompiler(fn, actualCallSiteType, newLocals(runtimeScope), null, null);
}
+ /**
+ * Returns a code installer for installing new code. If we're using either optimistic typing or loader-per-compile,
+ * then asks for a code installer with a new class loader; otherwise just uses the current installer. We use
+ * a new class loader with optimistic typing so that deoptimized code can get reclaimed by GC.
+ * @return a code installer for installing new code.
+ */
+ private CodeInstaller<ScriptEnvironment> getInstallerForNewCode() {
+ final ScriptEnvironment env = installer.getOwner();
+ return env._optimistic_types || env._loader_per_compile ? installer.withNewLoader() : installer;
+ }
+
Compiler getCompiler(final FunctionNode functionNode, final MethodType actualCallSiteType,
final ScriptObject runtimeScope, final Map<Integer, Type> invalidatedProgramPoints,
final int[] continuationEntryPoints) {
@@ -417,7 +429,7 @@
return new Compiler(
context,
context.getEnv(),
- installer,
+ getInstallerForNewCode(),
functionNode.getSource(), // source
context.getErrorManager(),
isStrict() | functionNode.isStrict(), // is strict
@@ -463,11 +475,12 @@
final TypeMap typeMap = typeMap(actualCallSiteType);
final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes);
- final StoredScript script = installer.loadScript(source, cacheKey);
+ final CodeInstaller<ScriptEnvironment> newInstaller = getInstallerForNewCode();
+ final StoredScript script = newInstaller.loadScript(source, cacheKey);
if (script != null) {
Compiler.updateCompilationId(script.getCompilationId());
- return install(script);
+ return installStoredScript(script, newInstaller);
}
}
@@ -481,15 +494,7 @@
return new FunctionInitializer(compiledFn, compiler.getInvalidatedProgramPoints());
}
-
- /**
- * Install this script using the given {@code installer}.
- *
- * @param script the compiled script
- * @return the function initializer
- */
- private FunctionInitializer install(final StoredScript script) {
-
+ private static Map<String, Class<?>> installStoredScriptClasses(final StoredScript script, final CodeInstaller<ScriptEnvironment> installer) {
final Map<String, Class<?>> installedClasses = new HashMap<>();
final Map<String, byte[]> classBytes = script.getClassBytes();
final String mainClassName = script.getMainClassName();
@@ -509,6 +514,17 @@
installedClasses.put(className, installer.install(className, bytecode));
}
+ return installedClasses;
+ }
+
+ /**
+ * Install this script using the given {@code installer}.
+ *
+ * @param script the compiled script
+ * @return the function initializer
+ */
+ private FunctionInitializer installStoredScript(final StoredScript script, final CodeInstaller<ScriptEnvironment> newInstaller) {
+ final Map<String, Class<?>> installedClasses = installStoredScriptClasses(script, newInstaller);
final Map<Integer, FunctionInitializer> initializers = script.getInitializers();
assert initializers != null;
@@ -523,7 +539,7 @@
}
}
- installer.initialize(installedClasses.values(), source, constants);
+ newInstaller.initialize(installedClasses.values(), source, constants);
initializer.setCode(installedClasses.get(initializer.getClassName()));
return initializer;
}