mirror of
https://github.com/google/guava.git
synced 2026-10-28 08:35:50 +08:00
Make the "direct" fallback in FinalizableReferenceQueue avoid reflection entirely.
I want to call attention up front to the fact that _`Finalizer`_ still uses reflection (to invoke `finalizeReferent`). We might imagine an alternative class similar to `Finalizer` that we'd use in the same-class-loader case. That class could call `finalizeReferent` directly. Hopefully it wouldn't need to [worry about class unloading](https://github.com/google/guava/blob/de2b19178d2748fe09402c531ef75350af634667/guava/src/com/google/common/base/internal/Finalizer.java#L70-L71), since we might be running under an environment in which that's not a thing? But this would deserve more thought. Anyway: The motivation here is that GraalVM needs help to see through reflection, just as Proguard, R8, etc. do. We haven't made much [effort to support Graal](https://github.com/google/guava/issues/3894), but this CL is an easy enough win. (Again, given that we _also_ use reflection in `Finalizer`, I wouldn't assume that `FinalizableReference`+GraalVM now "works." But it does now get through an initial step that I was testing against.) This CL might also allow us to simplify our Proguard configuration. However, it's not necessarily that simple: - Since we still _attempt_ reflection, we might want the class we reflect upon to still have its expected shape, even if we ultimately don't use it for one reason or another. - We could consider configuring Proguard with `-assumevalues` so that it _doesn't_ attempt reflection. (Compare https://github.com/google/guava/commit/14050c34d13b64846875d1a7b3ca4a1003a1e5cf, which I later undid in https://github.com/google/guava/commit/75da92419a7d414bd3de23c89f2ddfd83754767d.) - _Technically_, we should keep in mind that it's possible to use Proguard for a _server_ app, so "Proguard" does not imply "Android," and we ideally would write rules with that in mind. (This also came up in https://github.com/google/guava/commit/75da92419a7d414bd3de23c89f2ddfd83754767d.) In practice, I'm not sure how much that would matter. RELNOTES=n/a PiperOrigin-RevId: 884581412
This commit is contained in:
1 parent
199848a84f
commit
6fff6ef706
3 files changed
+92
-38
No files matched your search
@@ -187,12 +187,14 @@ public class FinalizableReferenceQueue implements Closeable {
|
||||
private static final String FINALIZER_CLASS_NAME = "com.google.common.base.internal.Finalizer";
|
||||
|
||||
/** Reference to Finalizer.startFinalizer(). */
|
||||
private static final Method startFinalizer;
|
||||
private static final FinalizerStarter finalizerStarter;
|
||||
|
||||
static {
|
||||
Class<?> finalizer =
|
||||
loadFinalizer(new SystemLoader(), new DecoupledLoader(), new DirectLoader());
|
||||
startFinalizer = getStartFinalizer(finalizer);
|
||||
Class<?> finalizer = loadFinalizer(new SystemLoader(), new DecoupledLoader());
|
||||
finalizerStarter =
|
||||
finalizer != null
|
||||
? new ReflectiveStarter(getStartFinalizer(finalizer))
|
||||
: new DirectStarter();
|
||||
}
|
||||
|
||||
/** The actual reference queue that our background thread will poll. */
|
||||
@@ -210,10 +212,8 @@ public class FinalizableReferenceQueue implements Closeable {
|
||||
frqRef = new PhantomReference<>(this, queue);
|
||||
boolean threadStarted = false;
|
||||
try {
|
||||
startFinalizer.invoke(null, FinalizableReference.class, queue, frqRef);
|
||||
finalizerStarter.startFinalizer(FinalizableReference.class, queue, frqRef);
|
||||
threadStarted = true;
|
||||
} catch (IllegalAccessException impossible) {
|
||||
throw new AssertionError(impossible); // startFinalizer() is public
|
||||
} catch (Throwable t) {
|
||||
logger.log(
|
||||
Level.INFO,
|
||||
@@ -257,11 +257,11 @@ public class FinalizableReferenceQueue implements Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through the given loaders until it finds one that can load Finalizer.
|
||||
* Iterates through the given loaders until it finds one that will load Finalizer.
|
||||
*
|
||||
* @return Finalizer.class
|
||||
* @return a {@link Class} for {@link Finalizer} or {@code null} if none of the loaders load it
|
||||
*/
|
||||
private static Class<?> loadFinalizer(FinalizerLoader... loaders) {
|
||||
private static @Nullable Class<?> loadFinalizer(FinalizerLoader... loaders) {
|
||||
for (FinalizerLoader loader : loaders) {
|
||||
Class<?> finalizer = loader.loadFinalizer();
|
||||
if (finalizer != null) {
|
||||
@@ -269,14 +269,15 @@ public class FinalizableReferenceQueue implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
throw new AssertionError();
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Loads Finalizer.class. */
|
||||
interface FinalizerLoader {
|
||||
|
||||
/**
|
||||
* Returns Finalizer.class or null if this loader shouldn't or can't load it.
|
||||
* Returns a {@link Class} for {@link Finalizer} or {@code null} if this loader shouldn't or
|
||||
* can't load it.
|
||||
*
|
||||
* @throws SecurityException if we don't have the appropriate privileges
|
||||
*/
|
||||
@@ -384,14 +385,38 @@ public class FinalizableReferenceQueue implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Finalizer} class from the current class loader. If that class gets used,
|
||||
* then we won't be able garbage collect this class loader, but at least the world doesn't end.
|
||||
*/
|
||||
private static final class DirectLoader implements FinalizerLoader {
|
||||
private interface FinalizerStarter {
|
||||
void startFinalizer(
|
||||
Class<?> finalizableReferenceClass,
|
||||
ReferenceQueue<Object> queue,
|
||||
PhantomReference<Object> frqRef)
|
||||
throws Throwable;
|
||||
}
|
||||
|
||||
private static final class ReflectiveStarter implements FinalizerStarter {
|
||||
final Method startFinalizer;
|
||||
|
||||
ReflectiveStarter(Method startFinalizer) {
|
||||
this.startFinalizer = startFinalizer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> loadFinalizer() {
|
||||
return Finalizer.class;
|
||||
public void startFinalizer(
|
||||
Class<?> finalizableReferenceClass,
|
||||
ReferenceQueue<Object> queue,
|
||||
PhantomReference<Object> frqRef)
|
||||
throws Throwable {
|
||||
startFinalizer.invoke(null, finalizableReferenceClass, queue, frqRef);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DirectStarter implements FinalizerStarter {
|
||||
@Override
|
||||
public void startFinalizer(
|
||||
Class<?> finalizableReferenceClass,
|
||||
ReferenceQueue<Object> queue,
|
||||
PhantomReference<Object> frqRef) {
|
||||
Finalizer.startFinalizer(finalizableReferenceClass, queue, frqRef);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -187,12 +187,14 @@ public class FinalizableReferenceQueue implements Closeable {
|
||||
private static final String FINALIZER_CLASS_NAME = "com.google.common.base.internal.Finalizer";
|
||||
|
||||
/** Reference to Finalizer.startFinalizer(). */
|
||||
private static final Method startFinalizer;
|
||||
private static final FinalizerStarter finalizerStarter;
|
||||
|
||||
static {
|
||||
Class<?> finalizer =
|
||||
loadFinalizer(new SystemLoader(), new DecoupledLoader(), new DirectLoader());
|
||||
startFinalizer = getStartFinalizer(finalizer);
|
||||
Class<?> finalizer = loadFinalizer(new SystemLoader(), new DecoupledLoader());
|
||||
finalizerStarter =
|
||||
finalizer != null
|
||||
? new ReflectiveStarter(getStartFinalizer(finalizer))
|
||||
: new DirectStarter();
|
||||
}
|
||||
|
||||
/** The actual reference queue that our background thread will poll. */
|
||||
@@ -210,10 +212,8 @@ public class FinalizableReferenceQueue implements Closeable {
|
||||
frqRef = new PhantomReference<>(this, queue);
|
||||
boolean threadStarted = false;
|
||||
try {
|
||||
startFinalizer.invoke(null, FinalizableReference.class, queue, frqRef);
|
||||
finalizerStarter.startFinalizer(FinalizableReference.class, queue, frqRef);
|
||||
threadStarted = true;
|
||||
} catch (IllegalAccessException impossible) {
|
||||
throw new AssertionError(impossible); // startFinalizer() is public
|
||||
} catch (Throwable t) {
|
||||
logger.log(
|
||||
Level.INFO,
|
||||
@@ -257,11 +257,11 @@ public class FinalizableReferenceQueue implements Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through the given loaders until it finds one that can load Finalizer.
|
||||
* Iterates through the given loaders until it finds one that will load Finalizer.
|
||||
*
|
||||
* @return Finalizer.class
|
||||
* @return a {@link Class} for {@link Finalizer} or {@code null} if none of the loaders load it
|
||||
*/
|
||||
private static Class<?> loadFinalizer(FinalizerLoader... loaders) {
|
||||
private static @Nullable Class<?> loadFinalizer(FinalizerLoader... loaders) {
|
||||
for (FinalizerLoader loader : loaders) {
|
||||
Class<?> finalizer = loader.loadFinalizer();
|
||||
if (finalizer != null) {
|
||||
@@ -269,14 +269,15 @@ public class FinalizableReferenceQueue implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
throw new AssertionError();
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Loads Finalizer.class. */
|
||||
interface FinalizerLoader {
|
||||
|
||||
/**
|
||||
* Returns Finalizer.class or null if this loader shouldn't or can't load it.
|
||||
* Returns a {@link Class} for {@link Finalizer} or {@code null} if this loader shouldn't or
|
||||
* can't load it.
|
||||
*
|
||||
* @throws SecurityException if we don't have the appropriate privileges
|
||||
*/
|
||||
@@ -383,14 +384,38 @@ public class FinalizableReferenceQueue implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Finalizer} class from the current class loader. If that class gets used,
|
||||
* then we won't be able garbage collect this class loader, but at least the world doesn't end.
|
||||
*/
|
||||
private static final class DirectLoader implements FinalizerLoader {
|
||||
private interface FinalizerStarter {
|
||||
void startFinalizer(
|
||||
Class<?> finalizableReferenceClass,
|
||||
ReferenceQueue<Object> queue,
|
||||
PhantomReference<Object> frqRef)
|
||||
throws Throwable;
|
||||
}
|
||||
|
||||
private static final class ReflectiveStarter implements FinalizerStarter {
|
||||
final Method startFinalizer;
|
||||
|
||||
ReflectiveStarter(Method startFinalizer) {
|
||||
this.startFinalizer = startFinalizer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> loadFinalizer() {
|
||||
return Finalizer.class;
|
||||
public void startFinalizer(
|
||||
Class<?> finalizableReferenceClass,
|
||||
ReferenceQueue<Object> queue,
|
||||
PhantomReference<Object> frqRef)
|
||||
throws Throwable {
|
||||
startFinalizer.invoke(null, finalizableReferenceClass, queue, frqRef);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DirectStarter implements FinalizerStarter {
|
||||
@Override
|
||||
public void startFinalizer(
|
||||
Class<?> finalizableReferenceClass,
|
||||
ReferenceQueue<Object> queue,
|
||||
PhantomReference<Object> frqRef) {
|
||||
Finalizer.startFinalizer(finalizableReferenceClass, queue, frqRef);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
# Proguard is intelligent enough to spot the use of reflection onto this, so we
|
||||
# only need to keep the names, and allow it to be stripped out if
|
||||
# FinalizableReferenceQueue is unused.
|
||||
#
|
||||
# (Under Android, we end up using Finalzier non-reflectively, so we could
|
||||
# likely rework all this configuration.)
|
||||
-keepclassmembernames class com.google.common.base.internal.Finalizer {
|
||||
*** startFinalizer(...);
|
||||
}
|
||||
@@ -25,6 +28,7 @@
|
||||
-keepclassmembers class com.google.common.base.internal.Finalizer {
|
||||
*** startFinalizer(...);
|
||||
}
|
||||
|
||||
-keepclassmembernames class com.google.common.base.FinalizableReference {
|
||||
void finalizeReferent();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user