When to initialize the method's class for MethodHandles.Lookup.findStatic()?
David Holmes
david.holmes at oracle.com
Fri Mar 18 02:38:13 UTC 2022
On 18/03/2022 10:51 am, Cheng Jin wrote:
> Hi Raffaello,
>
> My concern is why the verification happens even without initialization in the test without mh.invoke() in the main(), which I don't think is covered or explained in the JVM Spec.
> Put it in another way, my understanding is, when the class gets loaded, it is verified which doesn't necessarily lead to initialization, am I correct?
From Section 5.4 of the JVMS:
This specification allows an implementation flexibility as to when
linking activities (and, because of recursion, loading) take place,
provided that all of the following properties are maintained:
• A class or interface is completely loaded before it is linked.
• A class or interface is completely verified and prepared before it is
initialized.
---
Hotspot does loading, verification and preparing upfront/eagerly.
HTH.
David
-----
> Best Regards
> Cheng
>
> -----Original Message-----
> From: core-libs-dev <core-libs-dev-retn at openjdk.java.net> On Behalf Of Raffaello Giulietti
> Sent: March 17, 2022 8:35 PM
> To: core-libs-dev at openjdk.java.net
> Subject: [EXTERNAL] Re: When to initialize the method's class for MethodHandles.Lookup.findStatic()?
>
> Cheng,
>
> initialization is the last thing that happens because it's where user provided code gets executed.
>
> This has always been this way, as long as I can remember. See the JVMS for the gory details.
>
>
> Greetings
> Raffaello
>
>
> On 2022-03-18 01:21, Cheng Jin wrote:
>> Hi David,
>>
>> 1) for the test with mh.invoke() in main(), the log shows:
>> [0.262s][info][class,init] Start class verification for: Test_1
>> [0.262s][info][class,init] End class verification for: Test_1
>> [0.263s][info][class,init] 282 Initializing 'Test_1' (0x0000000800c00800)
>> [0.263s][info][class,init] Start class verification for: Test_2
>> [0.263s][info][class,init] End class verification for: Test_2
>> [0.272s][info][class,init] 366 Initializing 'Test_2' (0x0000000800c00a08) <------
>>
>> 2) for the test without mh.invoke() in main(), the log shows:
>> [0.296s][info][class,init] Start class verification for: Test_1
>> [0.296s][info][class,init] End class verification for: Test_1
>> [0.297s][info][class,init] 282 Initializing 'Test_1' (0x0000000800c00800)
>> [0.297s][info][class,init] Start class verification for: Test_2
>> [0.297s][info][class,init] End class verification for: Test_2
>> (Test_2 was verified but didn't get initialized)
>>
>> The comparison above literally surprised me that the bytecode verification happened prior to the class initialization, which means
>> the class got verified at first even without initialization coz I previously thought the initialization should trigger the verification rather than in the reversed order.
>>
>> Could you explain a little more about why it goes in this way?
>>
>> Best Regards
>> Cheng
>>
>>
>> -----Original Message-----
>> From: core-libs-dev <core-libs-dev-retn at openjdk.java.net> On Behalf Of David Holmes
>> Sent: March 17, 2022 7:46 PM
>> To: Raffaello Giulietti <raffaello.giulietti at gmail.com>; core-libs-dev at openjdk.java.net
>> Subject: [EXTERNAL] Re: When to initialize the method's class for MethodHandles.Lookup.findStatic()?
>>
>> Run with -Xlog:class+init=info to see the classes that get initialized and in what order.
>>
>> David
>>
>> On 18/03/2022 5:53 am, Raffaello Giulietti wrote:
>>> Hi again,
>>>
>>> here's code that shows that initialization doesn't happen during
>>> lookup but only upon invoking the method handle. (I'm on Java 17.)
>>>
>>> As long as the 2nd line in main() is commented, you don't see the
>>> message "Test_2 initialized", which shows that the lookup doesn't
>>> initialize Test_2.
>>> When you uncomment the line in main(), the message will appear.
>>>
>>> So, as advertised, it's the invocation of the method handle that can
>>> trigger initialization, not the lookup.
>>>
>>>
>>> HTH
>>> Raffaello
>>>
>>> ----
>>>
>>> import java.lang.invoke.*;
>>>
>>> public class Test_1 {
>>>
>>> static MethodHandle mh;
>>>
>>> static {
>>> try {
>>> mh = MethodHandles.lookup().findStatic(Test_2.class,
>>> "testMethod", MethodType.methodType(int.class, int.class));
>>> } catch (NoSuchMethodException | IllegalAccessException e) {
>>> e.printStackTrace();
>>> }
>>> }
>>>
>>> public static void main(String[] args) throws Throwable {
>>> System.out.println(mh);
>>> // System.out.println(Test_1.mh.invoke(0));
>>> }
>>>
>>> }
>>>
>>>
>>>
>>>
>>> public class Test_2 {
>>>
>>> static {
>>> System.out.println("Test_2 initialized");
>>> }
>>>
>>> static int testMethod(int value) { return (value + 1); }
>>>
>>> }
>>>
>>>
>>>
>>>
>>> On 2022-03-17 20:38, Raffaello Giulietti wrote:
>>>> Hi,
>>>>
>>>> as far as I can see, the code should not even compile, as there's a
>>>> static field in Test_1 which is initialized with an expression that
>>>> throws checked exceptions (findStatic(..)).
>>>>
>>>> In addition, it seems to me that there's nothing in your code that
>>>> reveals whether Test_2 has been initialized during the lookup. How
>>>> can you tell?
>>>>
>>>> Finally, the method handle invocation in Test_1 will throw, as you
>>>> don't pass any argument to a handle that expects one.
>>>>
>>>> Can you perhaps add more details?
>>>>
>>>>
>>>> Greetings
>>>> Raffaello
>>>>
>>>>
>>>>
>>>> On 2022-03-17 17:42, Cheng Jin wrote:
>>>>> Hi there,
>>>>>
>>>>> The document of
>>>>> INVALID URI REMOVED
>>>>> _en_java_javase_17_docs_api_java.base_java_lang_invoke_MethodHandles
>>>>> .Lookup.html-23findStatic-28java.lang.Class-2Cjava.lang.String-2Cjav
>>>>> a.lang.invoke.MethodType-29&d=DwIDaQ&c=jf_iaSHvJObTbx-siA1ZOg&r=X90f
>>>>> 3XIRXAH8hbNam6bIUlWfF_qUAezL9ue7M7bFuPQ&m=RvhguidNJ90V-HK-3Ctl-kUZE5
>>>>> cIfo_nt3_r8VZ0Fcc&s=tw_ph6oUkS0eCvzITWi9zEkarss5yNeHDrAIfvd3s3g&e=
>>>>> in the Java API is ambiguous in terms of when to initialize the
>>>>> method's class as follows (the same description as in other OpenJDK
>>>>> versions)
>>>>>
>>>>> If the returned method handle is invoked, the method's class will be
>>>>> initialized, if it has not already been initialized.
>>>>>
>>>>>
>>>>> It occurs to me that the method's class should be initialized when
>>>>> invoking the method handle but OpenJDK actually chooses to do the
>>>>> initialization in lookup.findStatic() rather than in mh.invoke()
>>>>> e.g.
>>>>> import java.lang.invoke.*;
>>>>>
>>>>> public class Test_1 {
>>>>> static MethodHandle mh =
>>>>> MethodHandles.lookup().findStatic(Test_2.class, "testMethod",
>>>>> MethodType.methodType(int.class, int.class)); <-----------
>>>>> Test_2.class gets initialized and verified.
>>>>>
>>>>> public static void main(String[] args) throws Throwable {
>>>>> Test_1.mh.invoke();
>>>>> }
>>>>> }
>>>>>
>>>>> public class Test_2 {
>>>>> static int testMethod(int value) { return (value + 1); } }
>>>>>
>>>>> So there should be more clear explanation what is the correct or
>>>>> expected behaviour at this point and why OpenJDK doesn't comply with
>>>>> the document to delay the initialization of the method's class to
>>>>> mh.invoke().
>>>>>
>>>>> Best Regards
>>>>> Cheng Jin
More information about the core-libs-dev
mailing list