<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>Hello Jaikiran, hello Viktor,<br>
      <br>
      in the meantime, I've seen that the JBS issue has been assigned to
      Viktor Klang. @Viktor: I totally agree with your comment that the
      proposed solution may not be the best possible option, and that
      further explorations were required.<br>
      <br>
      My intention to propose unlinking ConditionNodes by null'ing their
      ‘nextWaiter’ reference was just to verify that the chain of
      ‘nextWaiter’ references leads to the observed garbage collection
      behavior, and that the GC is able to collect the nodes during
      minor / young collections if the references are cleaned in time.<br>
      <br>
      I checked also a few other variants (null'ing the ‘nextWaiter’
      reference at the end of all await...() methods in ConditionObject,
      or in/just before enqueue()), but at the end of the day, I felt
      that null'ing it in doSignal() explains what I want to show the
      easiest. On the other hand, the other options could be better in
      order to avoid race conditions with canceled nodes.<br>
      <br>
      For sure there are many other options that I am not aware of, so
      please take my proposal just as an example.<br>
      <br>
      Looking forward to your explorations.</p>
    <p>Best regards</p>
    <p>Frank</p>
    <p><br>
    </p>
    <div class="moz-cite-prefix">Am 14.02.2024 um 07:43 schrieb Jaikiran
      Pai:<br>
    </div>
    <blockquote type="cite"
      cite="mid:59c3b1bc-fd60-4215-b8f7-96421f1881e2@gmail.com">
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <p>Hello Frank,</p>
      <p>I see that a JBS issue has been created for this same issue <a
          class="moz-txt-link-freetext"
          href="https://bugs.openjdk.org/browse/JDK-8325754"
          moz-do-not-send="true">https://bugs.openjdk.org/browse/JDK-8325754</a>.
        <br>
      </p>
      <p>I don't have enough knowledge of this area and haven't reviewed
        this part of the code in detail to see if there are any obvious
        issues with what you are proposing as a solution. Since there's
        now a JBS issue created for this and you seem to have done
        enough investigation and work on this one already, would you be
        interested in creating a pull request against the <a
          class="moz-txt-link-freetext"
          href="https://github.com/openjdk/jdk" moz-do-not-send="true">https://github.com/openjdk/jdk</a>
        repo with this proposed change? (you'll have to sign a OCA).
        This guide <a class="moz-txt-link-freetext"
          href="https://openjdk.org/guide/" moz-do-not-send="true">https://openjdk.org/guide/</a>
        should help you get started. It can then go through the usual
        reviews that a bug fix/enhancement goes through.<br>
      </p>
      <p>-Jaikiran<br>
      </p>
      <div class="moz-cite-prefix">On 11/02/24 7:27 pm, Frank Kretschmer
        wrote:<br>
      </div>
      <blockquote type="cite"
        cite="mid:6d3b62cb-15f4-471c-97b6-b72977223f91@gmx.net">
        <meta http-equiv="Content-Type"
          content="text/html; charset=UTF-8">
        <p>Hello Core-Libs-Dev team,<br>
          <br>
          may I ask you about your opinion about a tiny one-liner change
          in AbstractQueuedSynchronizer, just as a suggestion how to
          make ConditionObjects / Nodes even more garbage collector
          friendly?<br>
          <br>
          Checked out <a class="moz-txt-link-freetext"
href="https://github.com/openjdk/jdk/blob/jdk-17%2B35/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java"
            moz-do-not-send="true">https://github.com/openjdk/jdk/blob/jdk-17%2B35/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java</a>
          (the same on master branch with different line numbers near to
          line 1506):<br>
          <br>
          <font size="2" face="Courier New, Courier, monospace">@@
            -1431,40 +1431,41 @@ public abstract class
            AbstractQueuedSynchronizer<br>
                 public class ConditionObject implements Condition,
            java.io.Serializable {<br>
                     // ...<br>
                     private void doSignal(ConditionNode first, boolean
            all) {<br>
                         while (first != null) {<br>
                             ConditionNode next = first.nextWaiter;<br>
            +                first.nextWaiter = null;  // GC-friendly:
            avoid chains of dead ConditionNodes<br>
                             if ((firstWaiter = next) == null)<br>
                                 lastWaiter = null;<br>
                             if ((first.getAndUnsetStatus(COND) &
            COND) != 0) {<br>
                                 enqueue(first);<br>
                             // ...</font><br>
          <br>
          By setting the nextWaiter to null of the first condition node,
          which is transferred from the condition queue to the sync
          queue in this method, long chains of ConditionNode instances
          can be avoided. Though a single ConditionNode is small, these
          chains of ConditionNodes become very huge on the heap (I've
          seen more than 1GB on an application server over time) if at
          least one node was promoted to the old generation for any
          reason. They survive minor collections and are cleaned up only
          on mixed / full collections, and thus put unnecessary pressure
          on G1 garbage collector.<br>
          <br>
          The same change could also be applied to
          'AbstractQueuedLongSynchronizer'.<br>
          <br>
          I know premature optimization is the root of all evil, on the
          other hand I could image that many applications benefit from
          GC-friendly ConditionObjects, since they are frequently used
          in various classes like PriorityBlockingQueue /
          LinkedBlockingDeque / LinkedBlockingQueue, the latter one as
          default work queue for executor services like fixed thread
          pools for processing asynchronous tasks.<br>
          <br>
          Thank you all for your time and help!<br>
          <br>
          Best regards<br>
          Frank<br>
        </p>
        <div class="moz-cite-prefix">Am 08.02.2024 um 12:15 schrieb
          Frank Kretschmer:<br>
        </div>
        <blockquote type="cite"
          cite="mid:64bc8cbd-78f4-44f4-90ae-80466c563281@gmx.net">Hello
          Thomas, hello Core-Libs-Dev, <br>
          <br>
          thank you for cc'ing my email. In deed my idea/suggestion is
          to modify <br>
          the AbstractQueuedSynchronizer$ConditionNode handling in such
          a way that <br>
          it gets unlinked from the chain of condition nodes if it is
          not needed <br>
          any more (it might be the "nextWaiter" node), in order to be
          more <br>
          GC-friendly. <br>
          <br>
          @core-libs-dev: I've just attached the
          “G1LoiteringConditionNodes” demo <br>
          class and "gc.log" again so that you can have a look if you
          like. <br>
          <br>
          Best regards <br>
          <br>
          Frank <br>
          <br>
          <br>
          Am 08.02.2024 um 11:04 schrieb Thomas Schatzl: <br>
          <blockquote type="cite">Hi, <br>
            <br>
              since this looks like a suggestion for a change to the
            libraries <br>
            similar to the mentioned JDK-6805775, and not actually GC,
            cc'ing the <br>
            core-libs-dev mailing list. <br>
            <br>
            Hth, <br>
              Thomas <br>
            <br>
            On 07.02.24 15:20, Frank Kretschmer wrote: <br>
            <blockquote type="cite">Hi Java GC-experts, <br>
              <br>
              I'm facing an interesting G1 garbage collector observation
              in OpenJDK <br>
              17.0.9+9, which I would like to share with you. <br>
              <br>
              My application runs many asynchronous tasks in a fixed
              thread pool, <br>
              utilizing its standard LinkedBlockingQueue. Usually, it
              generates just a <br>
              little garbage, but from time to time, I observed that the
              survivor <br>
              spaces grow unexpectedly, and minor collection times
              increase. <br>
              <br>
              This being the case, many <br>
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode <br>
              instances can be found on the heap. In fact, the whole
              heap (rank 1 as <br>
              shown in jmap) was filled up with ConditionNode instances
              after a while. <br>
              <br>
              After some tests, I figured out that G1 seems to be able
              to collect <br>
              “dead” ConditionNode instances during minor collections
              only if no <br>
              formerly alive ConditionNode instances were promoted to
              the old <br>
              generation and died there. <br>
              <br>
              To illustrate that, I've attached a
              “G1LoiteringConditionNodes” class <br>
              that can be run for demo purposes, e.g. under Linux with
              OpenJDK <br>
              17.0.9+9 (VM options see comments within the class), and
              its gc-log <br>
              output. It shows that during the first two minutes,
              everything is fine, <br>
              but after a promotion to the old generation, survivors
              grow and minor <br>
              pause time increase from 3 to 10ms. <br>
              <br>
              For me, it looks like an issue similar to <br>
              <a class="moz-txt-link-freetext"
                href="https://bugs.openjdk.org/browse/JDK-6805775"
                moz-do-not-send="true">https://bugs.openjdk.org/browse/JDK-6805775</a>
              “LinkedBlockingQueue Nodes <br>
              should unlink themselves before becoming garbage”, which
              was fixed in <br>
              OpenJDK 7. <br>
              <br>
              What’s your opinion about that? Wouldn’t it be worth to
              enable G1 to <br>
              collect those AbstractQueuedSynchronizer$ConditionNode
              instances during <br>
              minor collections, as it is done for LinkedBlockingQueue
              Nodes? <br>
              <br>
              Best regards <br>
              <br>
              Frank<br>
            </blockquote>
            <br>
          </blockquote>
        </blockquote>
      </blockquote>
    </blockquote>
  </body>
</html>