Skip to content

Commit

Permalink
Prefer method handles over reflection [skip ci]
Browse files Browse the repository at this point in the history
This restores an optimization that was removed in #222 due to a memory
leak in older JDKs.
  • Loading branch information
ben-manes committed Jun 1, 2020
1 parent f95ff8d commit 1f9c248
Show file tree
Hide file tree
Showing 13 changed files with 73 additions and 657 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
package com.github.benmanes.caffeine.cache;

import static com.github.benmanes.caffeine.cache.Specifications.BOUNDED_LOCAL_CACHE;
import static com.github.benmanes.caffeine.cache.Specifications.BUILDER;
import static com.github.benmanes.caffeine.cache.Specifications.BUILDER_PARAM;
import static com.github.benmanes.caffeine.cache.Specifications.CACHE_LOADER;
import static com.github.benmanes.caffeine.cache.Specifications.CACHE_LOADER_PARAM;
import static com.github.benmanes.caffeine.cache.Specifications.kTypeVar;
import static com.github.benmanes.caffeine.cache.Specifications.vTypeVar;
Expand All @@ -25,6 +27,8 @@
import static java.util.stream.Collectors.toList;

import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand Down Expand Up @@ -67,6 +71,7 @@
import com.google.googlejavaformat.java.Formatter;
import com.google.googlejavaformat.java.FormatterException;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
Expand All @@ -79,6 +84,16 @@
* @author [email protected] (Ben Manes)
*/
public final class LocalCacheFactoryGenerator {
static final FieldSpec LOOKUP = FieldSpec.builder(MethodHandles.Lookup.class, "LOOKUP")
.initializer("$T.lookup()", MethodHandles.class)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.build();
static final FieldSpec FACTORY = FieldSpec.builder(MethodType.class, "FACTORY")
.initializer("$T.methodType($T.class, $T.class, $T.class, $T.class)",
MethodType.class, void.class, BUILDER, CACHE_LOADER.rawType, TypeName.BOOLEAN)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.build();

final Feature[] featureByIndex = { null, null, Feature.LISTENING, Feature.STATS,
Feature.MAXIMUM_SIZE, Feature.MAXIMUM_WEIGHT, Feature.EXPIRE_ACCESS,
Feature.EXPIRE_WRITE, Feature.REFRESH_WRITE};
Expand All @@ -105,6 +120,7 @@ void generate() throws IOException {
.addModifiers(Modifier.FINAL)
.addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build());
addClassJavaDoc();
addConstants();
generateLocalCaches();
addFactoryMethods();
writeJavaFile();
Expand Down Expand Up @@ -228,6 +244,11 @@ private void addClassJavaDoc() {
.addJavadoc("\n@author [email protected] (Ben Manes)\n");
}

private void addConstants() {
factory.addField(LOOKUP);
factory.addField(FACTORY);
}

/** Returns an encoded form of the class name for compact use. */
private static String encode(String className) {
return Feature.makeEnumName(className)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@
*/
package com.github.benmanes.caffeine.cache;

import static com.github.benmanes.caffeine.cache.LocalCacheFactoryGenerator.FACTORY;
import static com.github.benmanes.caffeine.cache.LocalCacheFactoryGenerator.LOOKUP;
import static com.github.benmanes.caffeine.cache.Specifications.BOUNDED_LOCAL_CACHE;
import static com.github.benmanes.caffeine.cache.Specifications.BUILDER;
import static com.github.benmanes.caffeine.cache.Specifications.CACHE_LOADER;
import static com.github.benmanes.caffeine.cache.Specifications.LOCAL_CACHE_FACTORY;

import java.lang.reflect.Constructor;
import java.lang.invoke.MethodHandle;

import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.TypeName;

/**
* @author [email protected] (Ben Manes)
Expand Down Expand Up @@ -100,15 +99,15 @@ private LocalCacheSelectorCode selector() {
.beginControlFlow("try")
.addStatement("$T<?> clazz = $T.class.getClassLoader().loadClass(sb.toString())",
Class.class, LOCAL_CACHE_FACTORY)
.addStatement("$T<?> ctor = clazz.getDeclaredConstructor($T.class, $T.class, $T.class)",
Constructor.class, BUILDER, CACHE_LOADER.rawType, TypeName.BOOLEAN)
.add("@SuppressWarnings($S)\n", "unchecked")
.addStatement("$1T factory = ($1T) ctor.newInstance(builder, cacheLoader, async)",
.addStatement("$T handle = $N.findConstructor(clazz, $N)",
MethodHandle.class, LOOKUP, FACTORY)
.addStatement("return ($T) handle.invoke(builder, cacheLoader, async)",
BOUNDED_LOCAL_CACHE)
.addStatement("return factory")
.nextControlFlow("catch ($T e)", ReflectiveOperationException.class)
.addStatement("throw new $T(sb.toString(), e)", IllegalStateException.class)
.endControlFlow();
.nextControlFlow("catch ($T | $T e)", RuntimeException.class, Error.class)
.addStatement("throw e")
.nextControlFlow("catch ($T t)", Throwable.class)
.addStatement("throw new $T(sb.toString(), t)", IllegalStateException.class)
.endControlFlow();
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import static java.util.stream.Collectors.toList;

import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand Down Expand Up @@ -101,6 +103,15 @@
*/
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
public final class NodeFactoryGenerator {
static final FieldSpec LOOKUP = FieldSpec.builder(MethodHandles.Lookup.class, "LOOKUP")
.initializer("$T.lookup()", MethodHandles.class)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.build();
static final FieldSpec FACTORY = FieldSpec.builder(MethodType.class, "FACTORY")
.initializer("$T.methodType($T.class)", MethodType.class, void.class)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.build();

final List<NodeRule> rules = ImmutableList.of(new AddSubtype(), new AddConstructors(),
new AddKey(), new AddValue(), new AddMaximum(), new AddExpiration(), new AddDeques(),
new AddFactoryMethods(), new AddHealth(), new Finalize());
Expand Down Expand Up @@ -194,6 +205,9 @@ private void addConstants() {
nodeFactory.addField(FieldSpec.builder(rawReferenceKeyType, DEAD_WEAK_KEY, modifiers)
.initializer("new $T(null, null)", rawReferenceKeyType)
.build());

nodeFactory.addField(FACTORY);
nodeFactory.addField(LOOKUP);
}

private void addKeyMethods() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
*/
package com.github.benmanes.caffeine.cache;

import static com.github.benmanes.caffeine.cache.NodeFactoryGenerator.FACTORY;
import static com.github.benmanes.caffeine.cache.NodeFactoryGenerator.LOOKUP;
import static com.github.benmanes.caffeine.cache.Specifications.NODE_FACTORY;

import java.lang.invoke.MethodHandle;

import com.squareup.javapoet.CodeBlock;

/**
Expand Down Expand Up @@ -96,12 +100,13 @@ private NodeSelectorCode selector() {
.beginControlFlow("try")
.addStatement("$T<?> clazz = $T.class.getClassLoader().loadClass(sb.toString())",
Class.class, NODE_FACTORY.rawType)
.add("@SuppressWarnings($S)\n", "unchecked")
.addStatement("$1T factory = ($1T) clazz.getDeclaredConstructor().newInstance()",
NODE_FACTORY)
.addStatement("return factory")
.nextControlFlow("catch ($T e)", ReflectiveOperationException.class)
.addStatement("throw new $T(sb.toString(), e)", IllegalStateException.class)
.addStatement("$T handle = $N.findConstructor(clazz, $N)",
MethodHandle.class, LOOKUP, FACTORY)
.addStatement("return ($T) handle.invoke()", NODE_FACTORY)
.nextControlFlow("catch ($T | $T e)", RuntimeException.class, Error.class)
.addStatement("throw e")
.nextControlFlow("catch ($T t)", Throwable.class)
.addStatement("throw new $T(sb.toString(), t)", IllegalStateException.class)
.endControlFlow();
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ private void addAccessExpiration() {
.addField(long.class, "accessTime", Modifier.VOLATILE)
.addMethod(newGetter(Strength.STRONG, TypeName.LONG, "accessTime", Visibility.PLAIN))
.addMethod(newSetter(TypeName.LONG, "accessTime", Visibility.PLAIN));
addVarHandle("accessTime", long.class);
addVarHandle("accessTime", TypeName.get(long.class));
addTimeConstructorAssignment(context.constructorByKey, "accessTime");
addTimeConstructorAssignment(context.constructorByKeyRef, "accessTime");
}
Expand All @@ -126,7 +126,7 @@ private void addWriteExpiration() {
.addField(long.class, "writeTime", Modifier.VOLATILE)
.addMethod(newGetter(Strength.STRONG, TypeName.LONG, "writeTime", Visibility.PLAIN))
.addMethod(newSetter(TypeName.LONG, "writeTime", Visibility.PLAIN));
addVarHandle("writeTime", long.class);
addVarHandle("writeTime", TypeName.get(long.class));
addTimeConstructorAssignment(context.constructorByKey, "writeTime");
addTimeConstructorAssignment(context.constructorByKeyRef, "writeTime");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import javax.lang.model.element.Modifier;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;

/**
Expand All @@ -40,7 +41,9 @@ protected void execute() {
.addField(newKeyField())
.addMethod(newGetter(keyStrength(), kTypeVar, "key", Visibility.PLAIN))
.addMethod(newGetRef("key"));
addVarHandle("key", Object.class);
addVarHandle("key", isStrongKeys()
? ClassName.get(Object.class)
: keyReferenceType().rawType);
}

private FieldSpec newKeyField() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import javax.lang.model.element.Modifier;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.MethodSpec;

Expand All @@ -47,7 +48,9 @@ protected void execute() {
.addMethod(newGetRef("value"))
.addMethod(makeSetValue())
.addMethod(makeContainsValue());
addVarHandle("value", Object.class);
addVarHandle("value", isStrongValues()
? ClassName.get(Object.class)
: valueReferenceType().rawType);
}

private FieldSpec newValueField() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,13 @@ protected boolean isStrongValues() {
|| context.generateFeatures.contains(Feature.STRONG_VALUES);
}

protected TypeName keyReferenceType() {
protected ParameterizedTypeName keyReferenceType() {
checkState(context.generateFeatures.contains(Feature.WEAK_KEYS));
return ParameterizedTypeName.get(
ClassName.get(PACKAGE_NAME + ".References", "WeakKeyReference"), kTypeVar);
}

protected TypeName valueReferenceType() {
protected ParameterizedTypeName valueReferenceType() {
checkState(!context.generateFeatures.contains(Feature.STRONG_VALUES));
String clazz = context.generateFeatures.contains(Feature.WEAK_VALUES)
? "WeakValueReference"
Expand All @@ -105,7 +105,7 @@ protected String varHandleName(String varName) {
}

/** Creates a VarHandle to the instance field. */
public void addVarHandle(String varName, Class<?> type) {
public void addVarHandle(String varName, TypeName type) {
String fieldName = varHandleName(varName);
context.nodeSubtype.addField(FieldSpec.builder(VarHandle.class, fieldName,
Modifier.PROTECTED, Modifier.STATIC, Modifier.FINAL).build());
Expand Down Expand Up @@ -177,7 +177,7 @@ private Strength strengthOf(Feature feature) {
}

protected enum Strength {
STRONG, WEAK, SOFT,
STRONG, WEAK, SOFT;
}

protected enum Visibility {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ final void expandOrRetry(E e, boolean wasUncontended) {
try {
probe = supplier.get();
break;
} catch (Throwable ignored) {}
} catch (Throwable ignored) { /* Try next strategy */ }
}
PROBE = requireNonNull(probe, "Unable to determine a probe strategy");
}
Expand Down
Loading

0 comments on commit 1f9c248

Please sign in to comment.