Details
-
Improvement
-
Status: Open
-
Minor
-
Resolution: Unresolved
-
4.0.13
-
None
-
None
Description
I observed an issue in my application where it is continuously churning (loading, then later unloading) LambdaForm MethodHandle classes.
Note: some discussion on this in https://lists.apache.org/thread/qmgsokwys9fkcozwq9vlmgnry14mdjwl.
[6.289s][info][class,init ] 2754 Initializing 'java/lang/invoke/LambdaForm$MH+0x0000000800e64400' (0x0000000800e64400) [6.289s][info][class,init ] 2755 Initializing 'java/lang/invoke/LambdaForm$MH+0x0000000800e64800' (0x0000000800e64800) [6.708s][info][class,nestmates ] LookupDefineClass: java/lang/invoke/LambdaForm$MH - non-nestmate, hidden, weak, with vm annotations [6.708s][info][class,nestmates ] LookupDefineClass: java/lang/invoke/LambdaForm$MH - non-nestmate, hidden, weak, with vm annotations [6.708s][info][class,load ] java.lang.invoke.LambdaForm$MH/0x0000000800e64c00 source: __JVM_LookupDefineClass__ [6.708s][info][class,load ] java.lang.invoke.LambdaForm$MH/0x0000000800e65000 source: __JVM_LookupDefineClass__ [6.708s][info][class,init ] 2756 Initializing 'java/lang/invoke/LambdaForm$MH+0x0000000800e64c00' (0x0000000800e64c00) [6.708s][info][class,init ] 2757 Initializing 'java/lang/invoke/LambdaForm$MH+0x0000000800e65000' (0x0000000800e65000) ... etc.
I have created a simple reproducer which demonstrates the issue.
// Run with // JAVA_OPTS="-Xlog:class*=info" groovy test.groovy # java11 or java17 // JAVA_OPTS="-XX:+TraceClassLoading" groovy test.groovy # java8 // Observe a steady stream of LambdaForm classes being loaded, forever. def foo(List<String> x) { } def bar(List<String> x) { foo(x) } List<String> l1 = new ArrayList(); List<String> l2 = new LinkedList(); while (true) { bar(l1) 127.times { bar(l2) } }
I'm using `Groovy Version: 4.0.13 JVM: 17.0.6-internal Vendor: Oracle Corporation OS: Linux`.
I originally noticed this because the class churning caused a memory leak (at least partially caused by [ https://bugs.openjdk.org/browse/JDK-8313678|https://bugs.openjdk.org/browse/JDK-8313678]),
but generally this much churn is bound to have negative performance implications. In my real application this is churning about 10 million classes per day.
I can avoid the issue when I spot it in my application by tweaking method arguments, but I'd be interested to know if this is a bug in Groovy and if it could
be fixed or there are any other more robust mitigations I can use.
I noticed https://issues.apache.org/jira/browse/GROOVY-8298 seems similar but not exactly the same, I think. The inline cache is not helping here, I'm not
sure if that's perhaps because the receiver is stable and it's the arguments which change?
I added logging to the sameClasses guard inserted in the invocation, and it's failing the guard because the argument types keep changing between LinkedList and ArrayList.
I believe this is a performance issue, as well as the high churn being likely to highlight bugs or issues in the JVM and things like attached profiling agents.