Groovy issue with GraalVM native-image for a large number of consecutive method calls

Damir Murat <[email protected]>
Newsgroups gmane.comp.lang.groovy.user
Message-ID <589b6bac-2a3d-41b8-854c-2074ed68d8c3@Spark>
I had an issue with Groovy and GraalVM native-image that occurs rarely but consistently.
It is especially problematic because it happens only at runtime without any warning from
the native-image builder.

I cannot claim I understand all underlying mechanics precisely, but I will try to explain it
according to my understanding.

Since I spent some time fighting with this thing, I thought it might be helpful for others to
know about it and maybe even mention the issue in Groovy documentation somewhere.
I did not find any mention of a similar problem online.

So, back to the problem. In the native image built with GraalVM, when executing some
methods repeatedly for a huge number of times (more than 10000 to be exact), Groovy
can cause the following exception:

  com.oracle.svm.core.jdk.UnsupportedFeatureError: Unsupported method java.lang.invoke.MethodHandleNatives.setCallSiteTargetNormal(CallSite, MethodHandle) is reachable

The exception is caused by

  org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:315)

method. Here is an example of a full stack trace:

11:41:22.349 [main] INFO o.k.t.g.s.r.r.GradleSourceRepackager - Repackaging Gradle sources: /Users/dmurat/work/dev/croz/klokwrk/klokwrk-project/modules/other/tool/klokwrk-tool-gradle-source-repack/gradle-8.5-all.zip ===> /Users/dmurat/.gradle/caches/8.5/generated-gradle-jars/gradle-api-8.5-sources.jar
Exception in thread "main" com.oracle.svm.core.jdk.UnsupportedFeatureError: Unsupported method java.lang.invoke.MethodHandleNatives.setCallSiteTargetNormal(CallSite, MethodHandle) is reachable
 at org.graalvm.nativeimage.builder/com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:121)
 at [email protected]/java.lang.invoke.MethodHandleNatives.setCallSiteTargetNormal(MethodHandleNatives.java)
 at [email protected]/java.lang.invoke.CallSite.setTargetNormal(CallSite.java:291)
 at [email protected]/java.lang.invoke.MutableCallSite.setTarget(MutableCallSite.java:155)
 at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:315)
 at [email protected]/java.lang.invoke.LambdaForm$DMH/s7887ae77.invokeStaticInit(LambdaForm$DMH)
 at org.klokwrk.tool.gradle.source.repack.repackager.GradleSourceRepackagerInfo.getGradleDistributionSrcDirPath(GradleSourceRepackagerInfo.groovy:61)
 at org.klokwrk.tool.gradle.source.repack.repackager.GradleSourceRepackager$_calculateCountOfTargetZipEntries_closure1$_closure4.doCall(GradleSourceRepackager.groovy:62)
 at [email protected]/java.lang.reflect.Method.invoke(Method.java:580)
 at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343)
 at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328)
 at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:279)
 at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1008)
 at groovy.lang.Closure.call(Closure.java:433)
 at org.codehaus.groovy.runtime.ConvertedClosure.invokeCustom(ConvertedClosure.java:52)
 at org.codehaus.groovy.runtime.ConversionHandler.invoke(ConversionHandler.java:113)
 at jdk.proxy1/jdk.proxy1.$Proxy50.test(Unknown Source)
 at [email protected]/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:178)
 at [email protected]/java.util.zip.ZipFile$EntrySpliterator.tryAdvance(ZipFile.java:573)
 at [email protected]/java.util.Spliterator.forEachRemaining(Spliterator.java:332)
 at [email protected]/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
 at [email protected]/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
 at [email protected]/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
 at [email protected]/java.util.stream.ReduceOps$5.evaluateSequential(ReduceOps.java:258)
 at [email protected]/java.util.stream.ReduceOps$5.evaluateSequential(ReduceOps.java:248)
 at [email protected]/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
 at [email protected]/java.util.stream.ReferencePipeline.count(ReferencePipeline.java:709)
 at org.klokwrk.tool.gradle.source.repack.repackager.GradleSourceRepackager$_calculateCountOfTargetZipEntries_closure1.doCall(GradleSourceRepackager.groovy:61)
 at [email protected]/java.lang.reflect.Method.invoke(Method.java:580)
 at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343)
 at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328)
 at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:279)
 at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1008)
 at groovy.lang.Closure.call(Closure.java:433)
 at groovy.lang.Closure.call(Closure.java:422)
 at org.codehaus.groovy.runtime.IOGroovyMethods.withCloseable(IOGroovyMethods.java:1616)
 at org.klokwrk.tool.gradle.source.repack.repackager.GradleSourceRepackager.calculateCountOfTargetZipEntries(GradleSourceRepackager.groovy:59)
 at org.klokwrk.tool.gradle.source.repack.repackager.GradleSourceRepackager.repackGradleSource(GradleSourceRepackager.groovy:53)
 at org.klokwrk.tool.gradle.source.repack.GradleSourceRepackCommand.run(GradleSourceRepackCommand.groovy:130)
 at picocli.CommandLine.executeUserObject(CommandLine.java:2026)
 at picocli.CommandLine.access$1500(CommandLine.java:148)
 at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2461)
 at picocli.CommandLine$RunLast.handle(CommandLine.java:2453)
 at picocli.CommandLine$RunLast.handle(CommandLine.java:2415)
 at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2273)
 at picocli.CommandLine$RunLast.execute(CommandLine.java:2417)
 at picocli.CommandLine.execute(CommandLine.java:2170)
 at io.micronaut.configuration.picocli.PicocliRunner.run(PicocliRunner.java:137)
 at io.micronaut.configuration.picocli.PicocliRunner.run(PicocliRunner.java:114)
 at org.klokwrk.tool.gradle.source.repack.GradleSourceRepackCommand.main(GradleSourceRepackCommand.groovy:64)


When looking at the source, the corresponding "if" branch is triggered, besides other things,
because "INDY_OPTIMIZE_THRESHOLD" is exceeded. After looking at the definition of the
constant, it is visible that it is read from the "groovy.indy.optimize.threshold" system property
and has a default of 10_000.

I tried to change the value to 100_000 by passing the property to the native executable, but
that does not work, unfortunately. The property must be set during native image building to
become effective. I've used the following options with native-builder:

 "-J-Dgroovy.indy.optimize.threshold=100000",
 "-J-Dgroovy.indy.fallback.threshold=100000"

As can be seen, besides "groovy.indy.optimize.threshold," there is also a related "groovy.indy.fallback.threshold"
system property set. After those changes, the created native image no longer throws the
exception mentioned above.

I hope this description will be helpful to anyone encountering similar issues.

Best regards,
Damir Murat
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.