Fwd: Question about Reflection details
Peter Levart
peter.levart at gmail.com
Tue May 15 21:08:39 UTC 2018
Hi Thomas,
On 05/15/18 14:20, Thomas Stüfe wrote:
> Hi all,
>
> Redirecting my question to core-libs as suggested by David.
>
> Thanks, Thomas
>
>
> ---------- Forwarded message ----------
> From: Thomas Stüfe <thomas.stuefe at gmail.com>
> Date: Tue, May 15, 2018 at 12:06 PM
> Subject: Question about Reflection details
> To: Hotspot dev runtime <hotspot-runtime-dev at openjdk.java.net>
>
>
> Hi all,
>
> I have two questions about the non-native reflection mechanism in the VM:
>
> 1) For each method invocation we generate a new child class of
> internal.reflect.MagicAccessorImpl, which is loaded by its own
> instance of DelegatingClassLoader.
That's not entirely true. Only the Nth invocation of a particular Method
generates it. Unless overridden by a system property, the 1st invocation
constructs a NativeMethodAccessorImpl and wraps it with
DelegatingMethodAccessorImpl which delegates invocations to the former
which uses JNI to perform the invocations and also counts them. By
default, after 15 invocations, NativeMethodAccessorImpl replaces itself
as the delegate of DelegatingMethodAccessorImpl with a generated
subclass of MethodAccessorImpl which is a subclass of MagicAccessorImpl
as you pointed out. So in reality, only the 15th invocation triggers
generation of new class and its loading in new class loader...
> The comment in jdk.internal.reflect.ClassDefiner states the reasons for this:
>
> <quote>
> There are two primary reasons for creating a new loader
> instead of defining these bytecodes directly into the defining
> loader of the target class: first, it avoids any possible
> security risk of having these bytecodes in the same loader.
> Second, it allows the generated bytecodes to be unloaded earlier
> than would otherwise be possible, decreasing run-time
> footprint.
> </quote>
>
> Do I understand this correctly:
>
> the lifetime of the MagicAccessorImpl instance, its class and its
> loading DelegatingClassLoader are defined by the lifetime of the
> java.lang.reflect.Method/Constructor object which keeps the
> MagicAccessorImpl instance in its methodAccessor/constructorAccessor
> field?
Right, but this same MethodAccessor instance is also set on the
corresponding "root" Method instance - this is the instance that is
never exposed to users, but is kept in per-declaring-Class cache. When
users ask for Method/Field/Constructor, they get a copy of the "root"
object, because Method/Field/Constructor are unfortunately mutable
objects (remember .setAccessible())...
So the MethodAccessor is also reachable from the "root" Method object,
which is softly reachable from the method's declaring Class. The cache
of Methods/Fields/Constructors in Class object is managed by a
SoftReference. So in theory, in the presence of memory pressure, cached
Methods/Fields/Constructors may be collected and their XxxAccessors with
them and the generated classes too.
> So, if that Method/Constructor object is collected, its
> MagicAccessorImpl instance is collected, and since it is the only
> instance its class too, and since it is the only class in that
> DelegatingClassLoader that gets collected as well?
Right.
> 2) I see in my traces that for each Method.invoke(obj.foo()) we generate
> - one GeneratedMethodAccessorImplXXX class living in its own
> DelegatingClassLoader instance, which invokes obj.foo
As described above, not in each invocation, but only in the 15th
invocation and never more for the same Method (unless it gets unloaded
in the meantime of course).
> - and then one additional GeneratedConstructorAccessorXXX, again
> living in its very own DelegatingClassLoader instance, which invokes
> the constructor for the just generated GeneratedMethodAccessorImplXXX.
This should not happen as that "newInstance()" invocation should be the
1st and the only invocation of the Constructor for that
GeneratedMethodAccessorImplXX subclass.
Unless you set a system property to disable inflation in which case the
GeneratedMethodAccessorImplXXX is generated on 1st invocation of the
Method and constructing its instance triggers generating
GeneratedConstructorAccessorXXX on its 1st invocation too. But only the
1st invocation. Following invocations of the same Method just use the
already instantiated accessor object.
> The latter I see only if I switch off inflation. The very first (and
> only) time GeneratedMethodAccessorImplXXX is instantiated it will
> cause GeneratedConstructorAccessorXXX to be created for that one
> newInstance() call.
Righ. Your observation is correct.
> But surely, in that case we could save one class loader and let the
> GeneratedConstructorAccessorXXX get loaded by the
> DelegatingClassLoader which also loaded the associated
> GeneratedMethodAccessorImplXXX? Or is it too much bother, since we are
> in an artificial scenario (noInflation=true)?
Better yet, GeneratedXXXAccessor(s) should be instantiated (constructed)
by special constructor accessors - just like the
GeneratedConstructorAccessorXXX(s) are -
BootstrapConstructorAccessorImpl is used for that purpose, to prevent
dependency loop. It would not be hard to re-purpose it to instantiate
all Generated[Field,Method,Constructor]AccessorXXX subclasse(s)
directly, using Unsafe.allocateInstance().
> ----
>
> In general, I have the following question:
>
> Will the 1:1 relationship between MagicAccessorImpl class and
> DelegatingClassLoader instance always hold true?
>
> Or is there work in progress somewhere which would maybe bundle
> MagicAccessorImpl classes in one loader (e.g. (2) would be a candidate
> for this), or maybe do it without loaders?
GeneratedMethodAccessor(s) as subclasses of MagicAccessorImpl are
treated specially by JVM verifier. Their bytecode is free to invoke any
method. Method.invoke API performs access checks on behalf of the
Method.invoke caller programatically, but the bytecode that does the
actual invocation lives in the GeneratedMethodAccessorXXX and that's why
it must be allowed to invoke any method. Having access to a Class object
representing a subclass of MagicAccessorImpl is a security risk since
instantiating such class gives the caller an object which is able to
invoke the method whichever that is (without the programmatic shield of
the Method object). Loading it in a special ClassLoader is therefore a
measure against exploiting such generated class as such class loader is
not in the delegating chain of the caller. and so caller can not resolve
it by name. Multiple GeneratedMethodAccessorXXX(s) for the method of the
same declaring class could be loaded by the same class loader though,
because that would usually be the granularity of clearing the softly
reachable cache of Methods in the Class object(s). But such optimization
has not been attempted yet, I suppose.
Another trick would be to use anonymous VM class-es to materialize the
code of GeneratedMethodAccessor(s). They could be defined by any
existing class loader and they would:
- not present a security risk as they are not resolvable by name
- be able to be unloaded before the defining class loader as they are
not retained by it
> Thanks!
>
> Thomas
Thanks for presenting the opportunity for optimization. Will you be
willing to try implementing it?
Regards, Peter
More information about the core-libs-dev
mailing list