Can't instantiate anonymous class more than 16 times

Wei Yin Teo weiyinteo at gmail.com
Tue Aug 11 12:03:58 PDT 2009



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/f8ae7a5d/attachment.html 


More information about the mlvm-dev mailing list