src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File jdk Cdiff src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java

src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java

Print this page
rev 11011 : 8057020: LambdaForm caches should support eviction
Reviewed-by: psandoz, ?

*** 23,32 **** --- 23,33 ---- * questions. */ package java.lang.invoke; + import java.lang.ref.SoftReference; import java.util.Arrays; import static java.lang.invoke.LambdaForm.*; import static java.lang.invoke.LambdaForm.BasicType.*; import static java.lang.invoke.MethodHandleImpl.Intrinsic; import java.util.Collections;
*** 56,69 **** /** A description of a cached transform, possibly associated with the result of the transform. * The logical content is a sequence of byte values, starting with a Kind.ordinal value. * The sequence is unterminated, ending with an indefinite number of zero bytes. * Sequences that are simple (short enough and with small enough values) pack into a 64-bit long. */ ! private static final class Transform { final long packedBytes; final byte[] fullBytes; - final LambdaForm result; // result of transform, or null, if there is none available private enum Kind { NO_KIND, // necessary because ordinal must be greater than zero BIND_ARG, ADD_ARG, DUP_ARG, SPREAD_ARGS, --- 57,69 ---- /** A description of a cached transform, possibly associated with the result of the transform. * The logical content is a sequence of byte values, starting with a Kind.ordinal value. * The sequence is unterminated, ending with an indefinite number of zero bytes. * Sequences that are simple (short enough and with small enough values) pack into a 64-bit long. */ ! private static final class Transform extends SoftReference<LambdaForm> { final long packedBytes; final byte[] fullBytes; private enum Kind { NO_KIND, // necessary because ordinal must be greater than zero BIND_ARG, ADD_ARG, DUP_ARG, SPREAD_ARGS,
*** 138,150 **** } Kind kind() { return Kind.values()[byteAt(0)]; } private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) { this.packedBytes = packedBytes; this.fullBytes = fullBytes; - this.result = result; } private Transform(long packedBytes) { this(packedBytes, null, null); assert(packedBytes != 0); } --- 138,150 ---- } Kind kind() { return Kind.values()[byteAt(0)]; } private Transform(long packedBytes, byte[] fullBytes, LambdaForm result) { + super(result); this.packedBytes = packedBytes; this.fullBytes = fullBytes; } private Transform(long packedBytes) { this(packedBytes, null, null); assert(packedBytes != 0); }
*** 241,261 **** } if (fullBytes != null) { buf.append("unpacked"); buf.append(Arrays.toString(fullBytes)); } if (result != null) { buf.append(" result="); buf.append(result); } return buf.toString(); } } /** Find a previously cached transform equivalent to the given one, and return its result. */ private LambdaForm getInCache(Transform key) { ! assert(key.result == null); // The transformCache is one of null, Transform, Transform[], or ConcurrentHashMap. Object c = lambdaForm.transformCache; Transform k = null; if (c instanceof ConcurrentHashMap) { @SuppressWarnings("unchecked") --- 241,262 ---- } if (fullBytes != null) { buf.append("unpacked"); buf.append(Arrays.toString(fullBytes)); } + LambdaForm result = get(); if (result != null) { buf.append(" result="); buf.append(result); } return buf.toString(); } } /** Find a previously cached transform equivalent to the given one, and return its result. */ private LambdaForm getInCache(Transform key) { ! assert(key.get() == null); // The transformCache is one of null, Transform, Transform[], or ConcurrentHashMap. Object c = lambdaForm.transformCache; Transform k = null; if (c instanceof ConcurrentHashMap) { @SuppressWarnings("unchecked")
*** 274,284 **** if (t == null) break; if (t.equals(key)) { k = t; break; } } } assert(k == null || key.equals(k)); ! return k == null ? null : k.result; } /** Arbitrary but reasonable limits on Transform[] size for cache. */ private static final int MIN_CACHE_ARRAY_SIZE = 4, MAX_CACHE_ARRAY_SIZE = 16; --- 275,285 ---- if (t == null) break; if (t.equals(key)) { k = t; break; } } } assert(k == null || key.equals(k)); ! return (k != null) ? k.get() : null; } /** Arbitrary but reasonable limits on Transform[] size for cache. */ private static final int MIN_CACHE_ARRAY_SIZE = 4, MAX_CACHE_ARRAY_SIZE = 16;
*** 291,301 **** Object c = lambdaForm.transformCache; if (c instanceof ConcurrentHashMap) { @SuppressWarnings("unchecked") ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c; Transform k = m.putIfAbsent(key, key); ! return k != null ? k.result : form; } assert(pass == 0); synchronized (lambdaForm) { c = lambdaForm.transformCache; if (c instanceof ConcurrentHashMap) --- 292,312 ---- Object c = lambdaForm.transformCache; if (c instanceof ConcurrentHashMap) { @SuppressWarnings("unchecked") ConcurrentHashMap<Transform,Transform> m = (ConcurrentHashMap<Transform,Transform>) c; Transform k = m.putIfAbsent(key, key); ! if (k == null) return form; ! LambdaForm result = k.get(); ! if (result != null) { ! return result; ! } else { ! if (m.replace(key, k, key)) { ! return form; ! } else { ! continue; ! } ! } } assert(pass == 0); synchronized (lambdaForm) { c = lambdaForm.transformCache; if (c instanceof ConcurrentHashMap)
*** 306,337 **** } Transform[] ta; if (c instanceof Transform) { Transform k = (Transform)c; if (k.equals(key)) { ! return k.result; } // expand one-element cache to small array ta = new Transform[MIN_CACHE_ARRAY_SIZE]; ta[0] = k; ! lambdaForm.transformCache = c = ta; } else { // it is already expanded ta = (Transform[])c; } int len = ta.length; int i; for (i = 0; i < len; i++) { Transform k = ta[i]; if (k == null) { break; } if (k.equals(key)) { ! return k.result; } } ! if (i < len) { // just fall through to cache update } else if (len < MAX_CACHE_ARRAY_SIZE) { len = Math.min(len * 2, MAX_CACHE_ARRAY_SIZE); ta = Arrays.copyOf(ta, len); lambdaForm.transformCache = ta; --- 317,366 ---- } Transform[] ta; if (c instanceof Transform) { Transform k = (Transform)c; if (k.equals(key)) { ! LambdaForm result = k.get(); ! if (result == null) { ! lambdaForm.transformCache = key; ! return form; ! } else { ! return result; ! } ! } else if (k.get() == null) { // overwrite stale entry ! lambdaForm.transformCache = key; ! return form; } // expand one-element cache to small array ta = new Transform[MIN_CACHE_ARRAY_SIZE]; ta[0] = k; ! lambdaForm.transformCache = ta; } else { // it is already expanded ta = (Transform[])c; } int len = ta.length; + int stale = -1; int i; for (i = 0; i < len; i++) { Transform k = ta[i]; if (k == null) { break; } if (k.equals(key)) { ! LambdaForm result = k.get(); ! if (result == null) { ! ta[i] = key; ! return form; ! } else { ! return result; } + } else if (stale < 0 && k.get() == null) { + stale = i; // remember 1st stale entry index } ! } ! if (i < len || stale >= 0) { // just fall through to cache update } else if (len < MAX_CACHE_ARRAY_SIZE) { len = Math.min(len * 2, MAX_CACHE_ARRAY_SIZE); ta = Arrays.copyOf(ta, len); lambdaForm.transformCache = ta;
*** 342,352 **** } lambdaForm.transformCache = m; // The second iteration will update for this query, concurrently. continue; } ! ta[i] = key; return form; } } } --- 371,382 ---- } lambdaForm.transformCache = m; // The second iteration will update for this query, concurrently. continue; } ! int idx = (stale >= 0) ? stale : i; ! ta[idx] = key; return form; } } }
src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File