Can't instantiate anonymous class more than 16 times

Rémi Forax forax at univ-mlv.fr
Mon Aug 10 18:09:07 PDT 2009


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

Another workaround, use method handle instead of reflection:
// creation
Constructor<?> c = testClass.getConstructor((Class<?>[])null);
MethodHandle mh = MethodHandles.lookup().unreflectConstrcutor(c);
// call
mh.invoke();




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 <mailto: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/b35d4fea/attachment.html 


More information about the mlvm-dev mailing list