Hi Zhengyu,
What is your usage pattern of these single-abstract-method implementations?
Since it sounds like you are creating a lot of them, are you storing them
in collections?
If you are keeping a lot of them in collection (say, as event handlers),
you may try to use `MethodHandleProxies.asInterfaceInstance` as a temporary
workaround on JDK 22 and higher (older version uses Proxy, which has
horrible invocation performance).
If you are on older versions from 15 to 21, unfortunately you might have to
write a hidden class for the same purpose or use an existing library. One
library that might be useful is https://github.com/LanternPowered/Lmbda
that effectively generates unloadable hidden classes, but its 3.x builds
are not maven central so you have to build yourself.

- Chen

On Wed, May 29, 2024 at 3:35 PM Zhengyu Gu <zhengyu...@servicenow.com>
wrote:

> Hi Chen,
>
>
>
> Thanks for the insights.
>
>
>
> We did refactor our code to avoid using LambdaMetaFactory,metafactory()
> directly.
>
>
>
> With increasing use of Lambdas, in our applications and libraries, the
> metaspace impact becomes a concern. If current implementation (not able to
> unload unused Lambda classes) here to stay, we must come up with a coding
> guideline to avoid excessive creation of Lambda classes,  any pointers or
> suggestions would be greatly appreciated.
>
>
>
> Best,
>
>
>
> -Zhengyu
>
>
>
> *From: *Chen Liang <liangchenb...@gmail.com>
> *Date: *Wednesday, May 29, 2024 at 2:43 PM
> *To: *Zhengyu Gu <zhengyu...@servicenow.com>
> *Cc: *core-libs-dev@openjdk.org <core-libs-dev@openjdk.org>
> *Subject: *Re: Question on Lambda function
> *[External Email]*
>
>
> ------------------------------
>
> Hi Gu,
>
> CallSite is specific to each invokedynamic instruction instead of each
> InvokeDynamic constant pool entry:
> https://docs.oracle.com/javase/specs/jvms/se22/html/jvms-6.html#jvms-6.5.invokedynamic
>
> And the linking is done by MethodHandleNatives.linkCallSite if you want to
> follow the Java implementation code.
>
> For why the lambda in the loop is constant, it's a feature from
> InnerClassLambdaMetafactory:
> https://github.com/openjdk/jdk/blob/c8eea59f508158075382079316cf0990116ff98e/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java#L236
>
> When the lambda is non-capturing, the bootstrap method
> LambdaMetafactory.metafactory will eagerly create a singleton instance and
> return this singleton in the indy instruction.
>
>
>
> Also, your metaspace pressure might be caused by the fact that Lambda
> classes (not instances) are no longer eagerly unloaded; see
> https://github.com/openjdk/jdk/pull/12493 and
> https://bugs.openjdk.org/browse/JDK-8302154. You are recommended to
> create your own facility to create hidden classes in Java 17 instead of
> continue to use LambdaMetafactory explicitly in code.
>
>
>
> Regards,
>
> Chen Liang
>
>
>
> On Wed, May 29, 2024 at 12:53 PM Zhengyu Gu <zhengyu...@servicenow.com>
> wrote:
>
> Hello Lambda experts,
>
>
>
> Since we upgraded JDK from 11 to 17, we’re experiencing metaspace
> pressure, largely due to Lambda class implementation changes.
>
>
>
> There’s a scenario (see attached test case),  that is especially puzzled
> me, hopefully, you can share some insights.
>
>
>
> In this test case, there is only one Lambda class is created inside the
> loop, but each one for the same functions outside loop.
>
>
>
> Example output:
>
>
>
> 0: Func =  LambdaFunc$$Lambda/0x00001f80000c4a20@4de8b406
>
> testMethod() called
>
> 1: Func =  LambdaFunc$$Lambda/0x00001f80000c4a20@4de8b406
>
> testMethod() called
>
> 2: Func =  LambdaFunc$$Lambda/0x00001f80000c4a20@4de8b406
>
> testMethod() called
>
> 3: Func =  LambdaFunc$$Lambda/0x00001f80000c4a20@4de8b406
>
> testMethod() called
>
> 4: Func =  LambdaFunc$$Lambda/0x00001f80000c4a20@4de8b406
>
> testMethod() called
>
>
>
> ….
>
>
>
> Outside loop1, Func =  LambdaFunc$$Lambda/0x00001f80000c4c58@402f32ff
>
> testMethod() called
>
> Outside loop2 Func = LambdaFunc$$Lambda/0x00001f80000d1000@5ae9a829
>
> testMethod() called
>
> Outside loop3 Func = LambdaFunc$$Lambda/0x00001f80000d1238@548b7f67
>
> testMethod() called
>
>
>
> And jcmd also confirmed there were 4 Lambda classes created:
>
>
>
>   49: CLD 0x000060000134cb50: "app" instance of
> jdk.internal.loader.ClassLoaders$AppClassLoader
>
>       Loaded classes:
>
>          1:    LambdaFunc$$Lambda/0x00001f80000d1238
>
>          2:    LambdaFunc$$Lambda/0x00001f80000d1000
>
>          3:    LambdaFunc$$Lambda/0x00001f80000c4c58
>
>          4:    LambdaFunc$$Lambda/0x00001f80000c4a20
>
>          5:    LambdaFunc
>
>
>
> Looking into bytecode, all four call sites have the same invokedynamic
> bytecode (invokedynamic #7,  0              // InvokeDynamic
> #0:run:()Ljava/lang/Runnable; ) and the first invokedynamic bytecode is
> inside the loop.
>
>
>
> But when I ran the program with -XX:+TraceBytecodes, it seems that the
> first invokedynamic was hoisted and result was used in the subsequence loop.
>
>
>
> Can anyone explain where this magic happens?  If the magic can apply to
> the instances outside the loop, so that only one Lambda class is created?
>
>
>
>
>
> Thank you for your time and expertise,
>
>
>
> -Zhengyu
>
>
>
>
>
>
>
>

Reply via email to