Can't instantiate anonymous class more than 16 times
Wei Yin Teo
weiyinteo at gmail.com
Tue Aug 11 12:15:26 PDT 2009
It doesn't work. :(
java.dyn.NoAccessException: receiver must be in caller class:
InstanceTest/27271057.<init>()void, from test.AnonymousClassLoading
at sun.dyn.MemberName.newNoAccessException(MemberName.java:421)
at java.dyn.MethodHandles.unreflectImpl(MethodHandles.java:496)
at java.dyn.MethodHandles
$Lookup.unreflectConstructor(MethodHandles.java:408)
at test.AnonymousClassLoading.testLoading(AnonymousClassLoading.java:
50)
On 11 Aug 2009, at 20:03, Wei Yin Teo wrote:
>
>
> On 11 Aug 2009, at 02:09, Rémi Forax wrote:
>
>> Le 11/08/2009 00:49, Wei Yin Teo a écrit :
>>>
>>> Right, I think the work around is working because we are getting a
>>> new class and we don't hit the threshold.
>>>
>>> The magic number 15 is the ReflectionFactory.inflationThreshold()
>>> as we can see in the class NativeConstructorAccessorImpl
>>>
>>>
>>> public Object newInstance(Object[] args) ... {
>>> if (++numInvocations >
>>> ReflectionFactory.inflationThreshold()) {
>>> ConstructorAccessorImpl acc = (ConstructorAccessorImpl)
>>> new MethodAccessorGenerator().
>>> generateConstructor(...)
>>> ...
>>>
>>> I think the AnonymousClassLoader mangles the class name from
>>> InstanceTest to InstanceTest/123456. However the generated
>>> ConstructorAccessor would interpret that as a class InstanceTest.
>>> 123456 instead of InstanceTest and the JVM would then try to load
>>> InstanceTest/123456.class. Of course it can't be found anywhere. I
>>> suppose Class.forName() should not be able to locate it in the
>>> system(permGen?) name space, either.
>>
>> You're right the problem is in package sun.reflect but the problem
>> is not
>> a dot transformed to a slash or vice-versa.
>>
>> Class.forName() uses class loader mechanism, and anonymous class
>> aren't registered
>> in any classloader cache. The reflection factory (here
>> MethodAccessorGenerator)
>> generates a bytecode with the name of the anonymous class in the
>> constant pool
>> to be able to call the constructor of the anonymous class.
>> When this bytecode is loaded, the VM tries to resolve it with a
>> classloader mechanism,
>> so it fails throwing a NoClassDefFound.
>>
>> So, short time workaround:
>> use -Dsun.reflect.noInflation=false
>
> Apparently, this doesn't work as you can see the above
> newInstance(..) calls
> MethodAccessorGenerator().generateConstructor(...) when the
> threshold is exceeded. Shouldn't it call
> ReflectionFactory.newConstructorAccessor(), which is guarded by the
> system property, instead?
>
>>
>> Another workaround, use method handle instead of reflection:
>> // creation
>> Constructor<?> c = testClass.getConstructor((Class<?>[])null);
>> MethodHandle mh = MethodHandles.lookup().unreflectConstrcutor(c);
>> // call
>> mh.invoke();
>>
> I'll try this.
>>
>>
>>
>> John, I wonder if the interface of AnonymousClassLoader should not
>> be changed to provide a method handle instead of a Class<?> ?
>>
>> Else, it remember sending a patch to add a method getHostClass() to
>> java.lang.Class or sun.misc.Unsafe to be able to know if a Class
>> is an anonymous class or not.
>>
>>
>>>
>>> On further note, I can't load an anonymous class which subclass
>>> another anonymous class too; because the class loader can't find
>>> the super class.
>>
>> Yes, for the same reason.
>>
>>> I am not too sure about how the class loading mechanism plays with
>>> anonymous classes, especially how do we maintain an anonymous
>>> class hierarchy.
>>
>> As explain above, you can't.
>> (technically, you can create a fake ClassLoader which contains all
>> your anonymous classes)
>> Anonymous classes like their Java counter-part are designed for
>> loading small final classes
>> like closures not for creating a whole hierarchy of anonymous
>> classes.
>>
>>
>>>
>>> Thanks,
>>> Wei
>>
>>
>> Cheers,
>> Rémi
>>
>>
>>>
>>>
>>> On 10 Aug 2009, at 23:19, Rémi Forax wrote:
>>>
>>>> Le 10/08/2009 23:54, Rémi Forax a écrit :
>>>>>
>>>>> Le 10/08/2009 21:56, Wei Yin Teo a écrit :
>>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> After loading an anonymous class through the
>>>>>> AnonymousClassLoader, I could construct the object by calling
>>>>>> the newInstance on the class. But the 16th time failed. Here is
>>>>>> the code and stack trace
>>>>>
>>>>> hum, perhaps a bug,
>>>>> could you try with :
>>>>>
>>>>> AnonymousClassLoader acl = new
>>>>> AnonymousClassLoader(LoadingTest.class);
>>>>>
>>>>> cheers,
>>>>> Rémi
>>>>
>>>> Forget what I've written.
>>>> I am able to reproduce the bug.
>>>> It seems you can't create more than 16 instances of an anonymous
>>>> class,
>>>> a really weird bug, isn't it.
>>>>
>>>> The known workaround is to create one class by instance :(
>>>>
>>>> ...
>>>> byte[] classBytes = getClassBytes();
>>>> for (int i = 0; i < 100; i++) {
>>>> Class<?> testClass = acl.loadClass(classBytes);
>>>> try {
>>>> testClass.newInstance();
>>>> }
>>>> ...
>>>>
>>>> Rémi
>>>>
>>>>>
>>>>>>
>>>>>> class LoadingTest {
>>>>>>
>>>>>> public void testLoading() {
>>>>>> AnonymousClassLoader acl = new AnonymousClassLoader();
>>>>>>
>>>>>> Class<?> testClass = acl.loadClass(getClassBytes());
>>>>>>
>>>>>> for (int i = 0; i < 100; i++) {
>>>>>> try {
>>>>>> testClass.newInstance();
>>>>>> }
>>>>>> catch(Throwable e) {
>>>>>> System.out.println("failed at " + i);
>>>>>> e.printStackTrace();
>>>>>> throw e;
>>>>>> }
>>>>>> }
>>>>>> }
>>>>>>
>>>>>> private byte[] getClassBytes() {
>>>>>> ClassWriter cw = new ClassWriter(0);
>>>>>> MethodVisitor mv;
>>>>>>
>>>>>> cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, "InstanceTest", null,
>>>>>> "java/lang/Object", null);
>>>>>> mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
>>>>>> mv.visitCode();
>>>>>> Label l0 = new Label();
>>>>>> mv.visitLabel(l0);
>>>>>> mv.visitVarInsn(ALOAD, 0);
>>>>>> mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object",
>>>>>> "<init>", "()V");
>>>>>> mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out",
>>>>>> "Ljava/io/PrintStream;");
>>>>>> mv.visitLdcInsn("constructed");
>>>>>> mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream",
>>>>>> "println",
>>>>>> "(Ljava/lang/String;)V");
>>>>>> mv.visitInsn(RETURN);
>>>>>> Label l1 = new Label();
>>>>>> mv.visitLabel(l1);
>>>>>> mv
>>>>>> .visitLocalVariable("this",
>>>>>> "Ltest/com/gs/s4j/misc/AnonymousClassLoading;", null,
>>>>>> l0, l1, 0);
>>>>>> mv.visitMaxs(2, 1);
>>>>>> mv.visitEnd();
>>>>>> cw.visitEnd();
>>>>>> return cw.toByteArray():
>>>>>> }
>>>>>> }
>>>>>>
>>>>>> It appears at the 16th time, the JVM generate a
>>>>>> ConstructorAccessor and it does not know how to handle
>>>>>> anonymous class.
>>>>>>
>>>>>> Thanks,
>>>>>> Wei
>>>>>>
>>>>>> Here is the output from the code.
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =
>>>>>> =================================================================
>>>>>> constructed
>>>>>> constructed
>>>>>> constructed
>>>>>> constructed
>>>>>> constructed
>>>>>> constructed
>>>>>> constructed
>>>>>> constructed
>>>>>> constructed
>>>>>> constructed
>>>>>> constructed
>>>>>> constructed
>>>>>> constructed
>>>>>> constructed
>>>>>> constructed
>>>>>> constructed
>>>>>> failed at 16
>>>>>> java.lang.NoClassDefFoundError: InstanceTest/7572081
>>>>>> at
>>>>>> sun.reflect.GeneratedConstructorAccessor3.newInstance(Unknown
>>>>>> Source)
>>>>>> at
>>>>>> sun
>>>>>> .reflect
>>>>>> .DelegatingConstructorAccessorImpl
>>>>>> .newInstance(DelegatingConstructorAccessorImpl.java:45)
>>>>>> at java.lang.reflect.Constructor.newInstance(Constructor.java:
>>>>>> 539)
>>>>>> at java.lang.Class.newInstance0(Class.java:372)
>>>>>> at java.lang.Class.newInstance(Class.java:325)
>>>>>> at
>>>>>> test
>>>>>> .com
>>>>>> .gs
>>>>>> .s4j
>>>>>> .misc
>>>>>> .AnonymousClassLoading.testLoading(AnonymousClassLoading.java:47)
>>>>>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>>>>>> at
>>>>>> sun
>>>>>> .reflect
>>>>>> .NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
>>>>>> 57)
>>>>>> at
>>>>>> sun
>>>>>> .reflect
>>>>>> .DelegatingMethodAccessorImpl
>>>>>> .invoke(DelegatingMethodAccessorImpl.java:43)
>>>>>> at java.lang.reflect.Method.invoke(Method.java:623)
>>>>>> at junit.framework.TestCase.runTest(TestCase.java:164)
>>>>>> at junit.framework.TestCase.runBare(TestCase.java:130)
>>>>>> at junit.framework.TestResult$1.protect(TestResult.java:106)
>>>>>> at junit.framework.TestResult.runProtected(TestResult.java:124)
>>>>>> at junit.framework.TestResult.run(TestResult.java:109)
>>>>>> at junit.framework.TestCase.run(TestCase.java:120)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .jdt
>>>>>> .internal
>>>>>> .junit
>>>>>> .runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:
>>>>>> 130)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .jdt.internal.junit.runner.TestExecution.run(TestExecution.java:
>>>>>> 38)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .jdt
>>>>>> .internal
>>>>>> .junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:
>>>>>> 467)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .jdt
>>>>>> .internal
>>>>>> .junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:
>>>>>> 683)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .jdt
>>>>>> .internal
>>>>>> .junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .pde
>>>>>> .internal
>>>>>> .junit
>>>>>> .runtime
>>>>>> .RemotePluginTestRunner.main(RemotePluginTestRunner.java:62)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .pde
>>>>>> .internal
>>>>>> .junit.runtime.CoreTestApplication.run(CoreTestApplication.java:
>>>>>> 23)
>>>>>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>>>>>> at
>>>>>> sun
>>>>>> .reflect
>>>>>> .NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
>>>>>> 57)
>>>>>> at
>>>>>> sun
>>>>>> .reflect
>>>>>> .DelegatingMethodAccessorImpl
>>>>>> .invoke(DelegatingMethodAccessorImpl.java:43)
>>>>>> at java.lang.reflect.Method.invoke(Method.java:623)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .equinox
>>>>>> .internal
>>>>>> .app
>>>>>> .EclipseAppContainer
>>>>>> .callMethodWithException(EclipseAppContainer.java:574)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .equinox
>>>>>> .internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .core
>>>>>> .runtime
>>>>>> .internal
>>>>>> .adaptor
>>>>>> .EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .core
>>>>>> .runtime
>>>>>> .internal
>>>>>> .adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:368)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
>>>>>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
>>>>>> at
>>>>>> sun
>>>>>> .reflect
>>>>>> .NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
>>>>>> 57)
>>>>>> at
>>>>>> sun
>>>>>> .reflect
>>>>>> .DelegatingMethodAccessorImpl
>>>>>> .invoke(DelegatingMethodAccessorImpl.java:43)
>>>>>> at java.lang.reflect.Method.invoke(Method.java:623)
>>>>>> at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:
>>>>>> 559)
>>>>>> at org.eclipse.equinox.launcher.Main.basicRun(Main.java:514)
>>>>>> at org.eclipse.equinox.launcher.Main.run(Main.java:1311)
>>>>>> at org.eclipse.equinox.launcher.Main.main(Main.java:1287)
>>>>>> Caused by: java.lang.ClassNotFoundException: InstanceTest.7572081
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .osgi
>>>>>> .internal
>>>>>> .loader.BundleLoader.findClassInternal(BundleLoader.java:494)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:
>>>>>> 410)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:
>>>>>> 398)
>>>>>> at
>>>>>> org
>>>>>> .eclipse
>>>>>> .osgi
>>>>>> .internal
>>>>>> .baseadaptor
>>>>>> .DefaultClassLoader.loadClass(DefaultClassLoader.java:105)
>>>>>> at java.lang.ClassLoader.loadClass(ClassLoader.java:331)
>>>>>> ... 41 more
>>>>>>
>>>>>>
>>>>>>
>>>>>> _______________________________________________
>>>>>> mlvm-dev mailing list
>>>>>> mlvm-dev at openjdk.java.net
>>>>>> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev
>>>>>>
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> mlvm-dev mailing list
>>>>> mlvm-dev at openjdk.java.net
>>>>> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev
>>>>>
>>>>
>>>> _______________________________________________
>>>> mlvm-dev mailing list
>>>> mlvm-dev at openjdk.java.net
>>>> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev
>>>
>>>
>>> _______________________________________________
>>> mlvm-dev mailing list
>>> mlvm-dev at openjdk.java.net
>>> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev
>>>
>>
>> _______________________________________________
>> mlvm-dev mailing list
>> mlvm-dev at openjdk.java.net
>> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/mlvm-dev/attachments/20090811/cc4d55db/attachment.html
More information about the mlvm-dev
mailing list