<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p><br>
    </p>
    <div class="moz-cite-prefix">On 12/6/25 09:30, Olivier Peyrusse
      wrote:<br>
    </div>
    <blockquote type="cite"
cite="mid:6ISsfBspHMRXGWyY5Ny7uRplatv-hLfKNRsISffFMjWwzBygAgyGh1kUPaVwfd7ckUukAcU5RHa27mU2BMTtBqq_YjE8tpu9BnegpPEQFjU=@proton.me">
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <div style="font-family: Arial, sans-serif; font-size: 14px;"><span><span>Hello
            community,</span></span>
        <div style="background-color: rgb(255, 255, 255);"><span><br>
          </span></div>
        <div style="background-color: rgb(255, 255, 255);"><span>Sorry
            if this is the wrong place to discuss internal classes such
            as the ForkJoinPool. If so, please, excuse me and point me
            in the right direction.</span></div>
        <div><br>
        </div>
        <div><span>At my company, we have experienced an unfortunate
            memory leak because one of our CountedCompleter was
            retaining a large object and the task was not released to
            the GC (I will give more details below but will first focus
            on the FJP code causing the issue).</span></div>
        <div><br>
        </div>
        <div><span>When running tasks, the FJP ends up calling </span><a
href="https://github.com/openjdk/jdk/blob/c419dda4e99c3b72fbee95b93159db2e23b994b6/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java#L1448-L1453"
            title="WorkQueue#topLevelExec" target="_blank"
            rel="noreferrer nofollow noopener" style=""
            moz-do-not-send="true"><span>WorkQueue#topLevelExec</span></a><span>,
            which is implemented as follow:</span></div>
        <div><br>
        </div>
        <div><span
style="font-family: Menlo, Consolas, "Courier New", monospace;"> 
                  final void topLevelExec(ForkJoinTask<?> task,
            int fifo) {</span>
          <div><span
style="font-family: Menlo, Consolas, "Courier New", monospace;"> 
                        while (task != null) {</span></div>
          <div><span
style="font-family: Menlo, Consolas, "Courier New", monospace;"> 
                            task.doExec();</span></div>
          <div><span
style="font-family: Menlo, Consolas, "Courier New", monospace;"> 
                            task = nextLocalTask(fifo);</span></div>
          <div><span
style="font-family: Menlo, Consolas, "Courier New", monospace;"> 
                        }</span></div>
          <span
style="font-family: Menlo, Consolas, "Courier New", monospace;"> 
                  }</span><br>
        </div>
        <div><span><br>
          </span></div>
        <div><font face="Arial, sans-serif">We can see that it starts
            from a top-level task </font><code>task</code><font
            face="Arial, sans-serif">​, executes it, and looks for the
            next task to execute before repeating this loop. This means
            that, as long as we find a task through </font><code>nextLocalTask</code>​<code></code><font
            face="Arial, sans-serif">​, we do not exit this method and
            the caller of <code>topLevelExec</code>​ retains in its
            stack a reference to the first executed task - like <a
href="https://github.com/openjdk/jdk/blob/c419dda4e99c3b72fbee95b93159db2e23b994b6/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java#L1992-L2019"
              title="here" target="_blank"
              rel="noreferrer nofollow noopener" moz-do-not-send="true">here</a>.
            This acts as a path from the GC root, preventing the garbage
            collection of the task.<br>
          </font></div>
      </div>
    </blockquote>
    <p>The issue is not in that code, but the calling sequence: A ref is
      retained mainly for the sake of a stack trace. The only way to
      (only sometimes) avoid this would be to manually inline the
      method, which leads to different compilation/execution issues,
      which leads to other tradeoffs impacting other usages. But it's
      worth considering. Thanks for the report.</p>
    <p>-Doug</p>
    <p><br>
    </p>
    <blockquote type="cite"
cite="mid:6ISsfBspHMRXGWyY5Ny7uRplatv-hLfKNRsISffFMjWwzBygAgyGh1kUPaVwfd7ckUukAcU5RHa27mU2BMTtBqq_YjE8tpu9BnegpPEQFjU=@proton.me">
      <div style="font-family: Arial, sans-serif; font-size: 14px;">
        <div><font face="Arial, sans-serif">So even if a
            CountedCompleter did complete its exec / tryComplete / etc,
            the framework will keep the object alive.</font></div>
        <div><font face="Arial, sans-serif">Could the code be changed to
            avoid this issue? I am willing to do the work, as well as
            come up with a test case reproducing the issue if it is
            deemed needed.</font></div>
        <div><font face="Arial, sans-serif"><br>
          </font></div>
        <div><font face="Arial, sans-serif">In our case, we were in the
            unfortunate situation where our counted completer was
            holding an element which happened to be a sort of head of a
            dynamic sort of linked queue. By retaining it, the rest of
            the growing linked queue was also retained in memory,
            leading to the memory leak.<br>
            Obvious fixes are possible in our code, by ensuring that we
            nullify such elements when our operations complete, and more
            ideas. But this means that we have to be constantly careful
            about the fields we pass to the task, what is captured if we
            give lambdas, etc. If the whole ForkJoinPool could also be
            improved to avoid such problems, it would be an additional
            safety.</font></div>
        <div><font face="Arial, sans-serif"><br>
          </font></div>
        <div><font face="Arial, sans-serif">Thank you for reading the
            mail</font></div>
        <div><font face="Arial, sans-serif">Cheers</font></div>
        <span><font face="Arial, sans-serif"><br>
          </font></span><span><font face="Arial, sans-serif">Olivier</font></span><br>
      </div>
      <div style="font-family: Arial, sans-serif; font-size: 14px;"
class="protonmail_signature_block protonmail_signature_block-empty">
        <div
class="protonmail_signature_block-user protonmail_signature_block-empty">
        </div>
        <div
class="protonmail_signature_block-proton protonmail_signature_block-empty">
        </div>
      </div>
    </blockquote>
  </body>
</html>