Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-11152

Indy churning through LambdaForm classes

    XMLWordPrintableJSON

Details

    • Improvement
    • Status: Open
    • Minor
    • Resolution: Unresolved
    • 4.0.13
    • None
    • Compiler
    • 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.

       

      Attachments

        Activity

          People

            Unassigned Unassigned
            olivergillespie Oliver Gillespie
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated: