Lambda class names no longer reported when listening for JVMTI_EVENT_CLASS_FILE_LOAD events

Peter Levart peter.levart at gmail.com
Fri Jan 24 09:38:20 PST 2014


On 01/24/2014 05:47 PM, Frost, Gary wrote:
> So is this an artifact of using the hash code of the class as the /XXXXXX
> suffix.

I think It's just a bug.

>
> As I mentioned below the classname in the bytes representing these VM
> anonymous classes do not contain the suffix whereas the reflected name
> does.
>   
> At first I was surprised, until I realized how non-trivial it was to
> create class bytes for a class whose name depends on the hashcode of the
> name of the class. It hurt my head thinking of this.
>
> I really think that the reflected name of all classes should match the
> name inside the classfile.  Aparapi can¹t be the only byte code inspection
> tool which makes this assumption.
>
> Gary

If I understand correctly, not only name (which is ignored and only 
reported mangled via reflection), but constants in constant pool can 
also differ between what's in the classfile bytes and what is actually 
used in runtime. For VM-anonymous classes the classfile bytes are just a 
template for internal class construction. You could use the same 
classfile bytes and create different VM-anonymous classes from them that 
would report the same ClassLoader but behave differently. Would you like 
them to report the same name and ClassLoader and behave differently?

So for code inspection tool to work correctly, It would also have to 
obtain the so called "constant pool patches" or obtain the constant pool 
entries from the runtime in some other way than by parsing the classfile 
bytes. See the javadoc of sun.misc.Unsafe.defineAnonymousClass() method 
for explanation:

http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/sun/misc/Unsafe.java

This might also help:

     https://blogs.oracle.com/jrose/entry/anonymous_classes_in_the_vm

Regards, Peter

>
> On 1/24/14, 10:26 AM, "Joel Borggrén-Franck" <joel.franck at oracle.com>
> wrote:
>
>> Hi Peter,
>>
>> Back then I filed https://bugs.openjdk.java.net/browse/JDK-8029100
>>
>> feel free to assign to you if you have a fix.
>>
>> cheers
>> /Joel
>>
>> On 24 Jan 2014, at 17:07, Peter Levart <peter.levart at gmail.com> wrote:
>>
>>> On 01/24/2014 12:48 AM, John Rose wrote:
>>>> Background on anonymous classes:
>>>>
>>>> They are a private feature of the HotSpot JVM implementation.
>>>> Naturally, compilers and other tightly-coupled tools have to know about
>>>> them.
>>>>
>>>> They don't have names, at least in the sense of something you could
>>>> use to look up via a class loader.  What names they display are (as you
>>>> see) derived from their bytecodes, but they do not function as regular
>>>> class names.
>>>>
>>>> Specifically, if someone tries to use the supposed name of an
>>>> anonymous class with Class.forName or ClassLoader.loadClass, the result
>>>> will be ClassNotFoundException.
>>>>
>>>> The mangled suffix /XXXXX on Class.getName string provides an extra
>>>> hint as to what is wrong.  (The XXXXX value happens to be the class's
>>>> hash code, which makes it easier to distinguish classes with the same
>>>> supposed name.)  Since slash '/' is an illegal element of class names,
>>>> there's no ambiguity about how the name got that way.
>>>>
>>>> HTH
>>>>
>>>> ‹ John
>>> Hi John,
>>>
>>> Not long ago I found a special corner case where the following
>>> expression:
>>>
>>>
>>>      Class.forName(clazz.getName(), clazz.getClassLoader()) == clazz
>>>
>>>
>>> ...evaluates to false. Here's an example that demonstrates this:
>>>
>>>
>>> public class AnnonClassTest {
>>>
>>>      static class C {}
>>>
>>>      public static void main(String[] args) throws Exception {
>>>          String cClassResourceName = C.class.getName().replace('.', '/')
>>> + ".class";
>>>          URL cClassUrl =
>>> C.class.getClassLoader().getResource(cClassResourceName);
>>>          byte[] bytes = new byte[4096];
>>>          try (InputStream in = cClassUrl.openStream();
>>> ByteArrayOutputStream out = new ByteArrayOutputStream()) {
>>>              for (int nread = in.read(bytes); nread >= 0; nread =
>>> in.read(bytes)) {
>>>                  out.write(bytes, 0, nread);
>>>              }
>>>              bytes = out.toByteArray();
>>>          }
>>>
>>>          Field uf = Unsafe.class.getDeclaredField("theUnsafe");
>>>          uf.setAccessible(true);
>>>          Unsafe u = (Unsafe) uf.get(null);
>>>
>>>          Class<?> cAnnonClass = u.defineAnonymousClass(C.class, bytes,
>>> new Object[0]);
>>>
>>>          System.out.println(C.class.getName());
>>>          System.out.println(cAnnonClass.getName());
>>>          System.out.println(C.class == cAnnonClass);
>>>          System.out.println();
>>>
>>>          Class<?> cArrayClass = C[].class;
>>>          Class<?> cAnnonArrayClass = Array.newInstance(cAnnonClass,
>>> 0).getClass();
>>>
>>>          System.out.println(cArrayClass.getName());
>>>          System.out.println(cAnnonArrayClass.getName());
>>>          System.out.println(cArrayClass == cAnnonArrayClass);
>>>          System.out.println();
>>>
>>>          Class<?> whichArrayClass =
>>> Class.forName(cAnnonArrayClass.getName());
>>>          System.out.println(whichArrayClass == cArrayClass);
>>>          System.out.println(whichArrayClass == cAnnonArrayClass);
>>>      }
>>> }
>>>
>>>
>>>
>>> This prints the following:
>>>
>>>
>>> AnnonClassTest$C
>>> AnnonClassTest$C/2101973421
>>> false
>>>
>>> [LAnnonClassTest$C;
>>> [LAnnonClassTest$C;
>>> false
>>>
>>> true
>>> false
>>>
>>>
>>> Do you agree it would be more correct if the name of the array of the
>>> anonymous class in above example was:
>>>
>>>
>>> [LAnnonClassTest$C/2101973421;
>>>
>>>
>>> This would work more consistently then (throwing ClassNotFoundException
>>> when trying to obtain such class by name).
>>>
>>>
>>> Regards, Peter
>>>
>>>
>>>>> Gary
>>>>>
>>>>> ________________________________________
>>>>> From: Brian Goetz [brian.goetz at oracle.com]
>>>>> Sent: Thursday, January 23, 2014 7:48 AM
>>>>> To: Frost, Gary
>>>>> Cc: lambda-dev at openjdk.java.net
>>>>> Subject: Re: Lambda class names no longer reported when listening for
>>>>> JVMTI_EVENT_CLASS_FILE_LOAD events
>>>>>
>>>>> Yes.  We moved to "anonymous classes" (in the sense of
>>>>> Unsafe.defineAnonymousClass, not anonymous inner classes), for reasons
>>>>> of both security and performance.  This is an unfortunate side effect.
>>>>>
>>>>> On Jan 22, 2014, at 5:33 PM, Frost, Gary wrote:
>>>>>
>>>>>> Not sure when this change came about but I just started using
>>>>>>
>>>>>> java version "1.8.0-ea"
>>>>>> Java(TM) SE Runtime Environment (build 1.8.0-ea-b124)
>>>>>>
>>>>>> I noticed that the JVMTI agent I use to  listen for
>>>>>> JVMTI_EVENT_CLASS_FILE_LOAD events is no longer seeing events for the
>>>>>> dynamically create classes created to encapsulate 'captured' args.
>>>>>> Or possibly the class events are triggeted, but the 'name' is NULL. I
>>>>>> am seeing an increase in the number of a events reported NULL as the
>>>>>> name.
>>>>>>
>>>>>> The Aparapi project listens for these events so we can determine the
>>>>>> captured args for a given lambda.  These are the synthetic classes
>>>>>> created on the fly by the method handle factory.
>>>>>>
>>>>>> This had been working fine until recently.  Is there a reason we
>>>>>> would stop generating  these, or is this just a regression?
>>>>>>
>>>>>> Gary
>>>>>>
>>>>>
>>>>>
>>>
>>
>



More information about the lambda-dev mailing list