# HG changeset patch # User attila # Date 1409747614 -7200 # Node ID 46647c4943ffb2db97324b457a567c44430c9ec2 # Parent 34c17c95665419ed76a98f5cf1210ed58eb2eca3 8056913: Limit the size of type info cache on disk Reviewed-by: jlaskey, lagergren diff -r 34c17c956654 -r 46647c4943ff src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java --- a/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java Thu Aug 28 16:10:38 2014 -0700 +++ b/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java Wed Sep 03 14:33:34 2014 +0200 @@ -31,9 +31,11 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.file.Files; +import java.nio.file.Path; import java.security.AccessController; import java.security.MessageDigest; import java.security.PrivilegedAction; @@ -41,6 +43,14 @@ import java.util.Base64; import java.util.Date; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Predicate; +import java.util.stream.Stream; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; @@ -49,30 +59,66 @@ import jdk.nashorn.internal.runtime.options.Options; /** - * Static utility that encapsulates persistence of decompilation information for functions. Normally, the type info - * persistence feature is enabled and operates in an operating-system specific per-user cache directory. You can - * override the directory by specifying it in the {@code nashorn.typeInfo.cacheDir} directory. Also, you can disable the - * type info persistence altogether by specifying the {@code nashorn.typeInfo.disabled} system property. + * Static utility that encapsulates persistence of type information for functions compiled with optimistic + * typing. With this feature enabled, when a JavaScript function is recompiled because it gets deoptimized, + * the type information for deoptimization is stored in a cache file. If the same function is compiled in a + * subsequent JVM invocation, the type information is used for initial compilation, thus allowing the system + * to skip a lot of intermediate recompilations and immediately emit a version of the code that has its + * optimistic types at (or near) the steady state. + *
+ * Normally, the type info persistence feature is disabled. When the {@code nashorn.typeInfo.maxFiles} system
+ * property is specified with a value greater than 0, it is enabled and operates in an operating-system
+ * specific per-user cache directory. You can override the directory by specifying it in the
+ * {@code nashorn.typeInfo.cacheDir} directory. The maximum number of files is softly enforced by a task that
+ * cleans up the directory periodically on a separate thread. It is run after some delay after a new file is
+ * added to the cache. The default delay is 20 seconds, and can be set using the
+ * {@code nashorn.typeInfo.cleanupDelaySeconds} system property. You can also specify the word
+ * {@code unlimited} as the value for {@code nashorn.typeInfo.maxFiles} in which case the type info cache is
+ * allowed to grow without limits.
*/
public final class OptimisticTypesPersistence {
+ // Default is 0, for disabling the feature when not specified. A reasonable default when enabled is
+ // dependent on the application; setting it to e.g. 20000 is probably good enough for most uses and will
+ // usually cap the cache directory to about 80MB presuming a 4kB filesystem allocation unit. There is one
+ // file per JavaScript function.
+ private static final int DEFAULT_MAX_FILES = 0;
+ // Constants for signifying that the cache should not be limited
+ private static final int UNLIMITED_FILES = -1;
+ // Maximum number of files that should be cached on disk. The maximum will be softly enforced.
+ private static final int MAX_FILES = getMaxFiles();
+ // Number of seconds to wait between adding a new file to the cache and running a cleanup process
+ private static final int DEFAULT_CLEANUP_DELAY = 20;
+ private static final int CLEANUP_DELAY = Math.max(0, Options.getIntProperty(
+ "nashorn.typeInfo.cleanupDelaySeconds", DEFAULT_CLEANUP_DELAY));
// The name of the default subdirectory within the system cache directory where we store type info.
private static final String DEFAULT_CACHE_SUBDIR_NAME = "com.oracle.java.NashornTypeInfo";
// The directory where we cache type info
- private static final File cacheDir = createCacheDir();
+ private static final File baseCacheDir = createBaseCacheDir();
+ private static final File cacheDir = createCacheDir(baseCacheDir);
// In-process locks to make sure we don't have a cross-thread race condition manipulating any file.
private static final Object[] locks = cacheDir == null ? null : createLockArray();
-
// Only report one read/write error every minute
private static final long ERROR_REPORT_THRESHOLD = 60000L;
private static volatile long lastReportedError;
-
+ private static final AtomicBoolean scheduledCleanup;
+ private static final Timer cleanupTimer;
+ static {
+ if (baseCacheDir == null || MAX_FILES == UNLIMITED_FILES) {
+ scheduledCleanup = null;
+ cleanupTimer = null;
+ } else {
+ scheduledCleanup = new AtomicBoolean();
+ cleanupTimer = new Timer(true);
+ }
+ }
/**
- * Retrieves an opaque descriptor for the persistence location for a given function. It should be passed to
- * {@link #load(Object)} and {@link #store(Object, Map)} methods.
+ * Retrieves an opaque descriptor for the persistence location for a given function. It should be passed
+ * to {@link #load(Object)} and {@link #store(Object, Map)} methods.
* @param source the source where the function comes from
* @param functionId the unique ID number of the function within the source
- * @param paramTypes the types of the function parameters (as persistence is per parameter type specialization).
+ * @param paramTypes the types of the function parameters (as persistence is per parameter type
+ * specialization).
* @return an opaque descriptor for the persistence location. Can be null if persistence is disabled.
*/
public static Object getLocationDescriptor(final Source source, final int functionId, final Type[] paramTypes) {
@@ -82,7 +128,8 @@
final StringBuilder b = new StringBuilder(48);
// Base64-encode the digest of the source, and append the function id.
b.append(source.getDigest()).append('-').append(functionId);
- // Finally, if this is a parameter-type specialized version of the function, add the parameter types to the file name.
+ // Finally, if this is a parameter-type specialized version of the function, add the parameter types
+ // to the file name.
if(paramTypes != null && paramTypes.length > 0) {
b.append('-');
for(final Type t: paramTypes) {
@@ -118,6 +165,11 @@
@Override
public Void run() {
synchronized(getFileLock(file)) {
+ if (!file.exists()) {
+ // If the file already exists, we aren't increasing the number of cached files, so
+ // don't schedule cleanup.
+ scheduleCleanup();
+ }
try (final FileOutputStream out = new FileOutputStream(file)) {
out.getChannel().lock(); // lock exclusive
final DataOutputStream dout = new DataOutputStream(new BufferedOutputStream(out));
@@ -174,19 +226,19 @@
}
}
- private static File createCacheDir() {
- if(Options.getBooleanProperty("nashorn.typeInfo.disabled")) {
+ private static File createBaseCacheDir() {
+ if(MAX_FILES == 0 || Options.getBooleanProperty("nashorn.typeInfo.disabled")) {
return null;
}
try {
- return createCacheDirPrivileged();
+ return createBaseCacheDirPrivileged();
} catch(final Exception e) {
getLogger().warning("Failed to create cache dir", e);
return null;
}
}
- private static File createCacheDirPrivileged() {
+ private static File createBaseCacheDirPrivileged() {
return AccessController.doPrivileged(new PrivilegedAction