Hi Oli,

from looking at our invokdedynamic code for a while I realized there are
actually two potential bugs in there for this case. I am not yet sure it
is related to what you see.

The bug I found is the following:
```
def foo(x) {
 x.getName()
}

foo(String)
foo(Integer)
```

The callsite in foo is having a Class instance as receiver. The
Methodhandle we create for the call checks for the receiver class to
stay the same, but the caching mechanism in front of the handle sees
here only Class as receiver. So for this cache the receiver does not
change and for the handle it does. The result is that for every such
call a new handle is produced. To show it with your code example:

```
def foo(Object x) {
     if (x.getClass().getName().equals("dummy")) print "dummy"
}
while (true) {
     foo("1")
     127.times { foo(1) }
}
```

The foo("1") will produce the first handle for getName in foo. The
foo(1) the second, which replaces the handle for foo("1"). Then foo(1)
is called 126 times more without replacement. Now the loop repeats and
foo("1") produces a new handle, followed by foo(1) creating a new handle
that lasts 127 calls till the loop repeats again. None of the handles
are reused. The cache supposed to solve this will have at maximum 1
element here.

Is it wrong for the handle to have this class check in there? Well, not
exactly. In Groovy it is perfectly valid to do

def x = Foo
x.bar()

to call a static method bar on the class Foo. So for static method calls
I see a bug in the caching. But in this case it is not a static method
call, it is a call on the instance Class, here arguably the handle
should not do the class check like it does.

If I remove the class receiver guard for testing I see a lot less of
those class,init messages


bye Jochen


On 05.08.23 16:27, Gillespie, Oli wrote:
I have simplified the reproducer slightly, no concurrency is needed.

```
// Reproducer for indy performance/classloading issue.
// Run with JAVA_OPTS="-Xlog:class*=info" groovy test.groovy,
// and observe a high volume of LambdaForm classes churning
// even during steady state.
def foo(Object x) {
    if (x.getClass().getName().equals("dummy")) print "dummy"
}
while (true) {
     foo("1")
     // 127 is just enough to trigger compilation of a new MethodHandle since
     // java.lang.invoke.MethodHandle.CUSTOMIZE_THRESHOLD defaults to 127.
     127.times { foo(1) }
}
```

It seems that every invocation where the argument types change causes 
IndyInterface to
generate a brand new MethodHandle for the invocation. The LambdaForm 
compilation then
happens when the new MethodHandle is used enough times to trigger customization.

I think some of this was discussed in 
https://mail.openjdk.org/pipermail/mlvm-dev/2017-May/006760.html,
but I don't fully understand how it applies to my case.



Amazon Development Centre (London) Ltd. Registered in England and Wales with 
registration number 04543232 with its registered office at 1 Principal Place, 
Worship Street, London EC2A 2FA, United Kingdom.




Reply via email to