<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=koi8-r">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Hi Simeon,</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Thanks for your email.</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div class="elementToProof"><span style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">After discussing this with Doug Lea and Alan Bateman I've opened the following PR:
<a href="https://github.com/openjdk/jdk/pull/18144" id="LPlnk">https://github.com/openjdk/jdk/pull/18144</a></span></div>
<div class="_Entity _EType_OWALinkPreview _EId_OWALinkPreview _EReadonly_1">
<div id="LPBorder_GTaHR0cHM6Ly9naXRodWIuY29tL29wZW5qZGsvamRrL3B1bGwvMTgxNDQ." class="LPBorder815039" style="width: 100%; margin-top: 16px; margin-bottom: 16px; position: relative; max-width: 800px; min-width: 424px;">
<table id="LPContainer815039" role="presentation" style="padding: 12px 36px 12px 12px; width: 100%; border-width: 1px; border-style: solid; border-color: rgb(200, 200, 200); border-radius: 2px;">
<tbody>
<tr style="border-spacing: 0px;" valign="top">
<td>
<div id="LPImageContainer815039" style="position: relative; margin-right: 12px; height: 120px; overflow: hidden; width: 240px;">
<a target="_blank" id="LPImageAnchor815039" href="https://github.com/openjdk/jdk/pull/18144"><img id="LPThumbnailImageId815039" alt="" style="display: block;" width="240" height="120" src="https://opengraph.githubassets.com/70f2d1af34c3f8422fb6cf90cf7d15d742843422bf90a10cf4c49bf729aea656/openjdk/jdk/pull/18144"></a></div>
</td>
<td style="width: 100%;">
<div id="LPTitle815039" style="font-size: 21px; font-weight: 300; margin-right: 8px; font-family: "wf_segoe-ui_light", "Segoe UI Light", "Segoe WP Light", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif; margin-bottom: 12px;">
<a target="_blank" id="LPUrlAnchor815039" href="https://github.com/openjdk/jdk/pull/18144" style="text-decoration: none;">8327501: Common ForkJoinPool prevents class unloading in some cases by viktorklang-ora ž Pull Request #18144 ž openjdk/jdk</a></div>
<div id="LPDescription815039" style="font-size: 14px; max-height: 100px; color: rgb(102, 102, 102); font-family: "wf_segoe-ui_normal", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif; margin-bottom: 12px; margin-right: 8px; overflow: hidden;">
The common ForkJoinPool creating threads as a result of submitting tasks is preventing class unloading when the thread construction is initiated from a class loaded in a separate classloader. This ...</div>
<div id="LPMetadata815039" style="font-size: 14px; font-weight: 400; color: rgb(166, 166, 166); font-family: "wf_segoe-ui_normal", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif;">
github.com</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div class="elementToProof" style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div id="Signature">
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Cheers,<br>
–</div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<b><br>
</b></div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
<b>Viktor Klang</b></div>
<div style="font-family: Calibri, Arial, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);">
Software Architect, Java Platform Group<br>
Oracle</div>
</div>
<div id="appendonsend"></div>
<hr style="display:inline-block;width:98%" tabindex="-1">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" style="font-size:11pt" color="#000000"><b>From:</b> core-libs-dev <core-libs-dev-retn@openjdk.org> on behalf of S A <simeon.danailov.andreev@gmail.com><br>
<b>Sent:</b> Monday, 4 March 2024 13:23<br>
<b>To:</b> core-libs-dev@openjdk.java.net <core-libs-dev@openjdk.java.net><br>
<b>Subject:</b> Class loader leaked when ForkJoinPool is used in loaded class, with Java 19+</font>
<div> </div>
</div>
<div>
<div dir="ltr">
<div dir="ltr">
<div>Hi all,</div>
<div><br>
</div>
<div>after moving our application to Java 21 (up from Java 17), we noticed a class loader leak. A memory snapshot points to a ProtectionDomain object held by a ForkJoinWorkerThread, the protection domain holds the class loader and prevents GC.</div>
<div><br>
</div>
<div>To reproduce outside of our application, we use this snippet:</div>
<div><br>
</div>
<div>import java.lang.ref.WeakReference;<br>
import java.net.MalformedURLException;<br>
import java.net.URL;<br>
import java.net.URLClassLoader;<br>
import java.nio.file.Paths;<br>
public class TestClassloaderLeak {<br>
    public static void main(String[] args) throws Exception {<br>
        WeakReference<Object> wr = load();<br>
        gc();<br>
        System.out.println("wr=" + wr.get());<br>
    }<br>
    private static void gc() {<br>
        System.gc();<br>
        System.runFinalization();<br>
    }<br>
    private static WeakReference<Object> load() throws Exception {<br>
        URLClassLoader cl = new URLClassLoader(new URL[] { url() }, TestClassloaderLeak.class.getClassLoader());<br>
        WeakReference<Object> wr = new WeakReference<>(cl);<br>
        Class<?> ca = cl.loadClass("A");<br>
        ca.getConstructor(String.class).newInstance("A");<br>
        cl.close();<br>
        return wr;<br>
    }<br>
    private static URL url() throws MalformedURLException {<br>
        return Paths.get("/data/tmp/testleak/lib/").toUri().toURL();<br>
    }<br>
}<br>
</div>
<div><br>
</div>
<div>import java.util.concurrent.ForkJoinPool;<br>
import java.util.concurrent.ForkJoinTask;<br>
public class A {<br>
    public final String s;<br>
    public A(String s) {<br>
        this.s = s;<br>
        ForkJoinTask<?> task = ForkJoinPool.commonPool().submit(() -> { System.out.println("A constructor"); });<br>
        try {<br>
            task.get();<br>
        } catch (Exception e) {<br>
            e.printStackTrace(System.out);<br>
        }<br>
    }<br>
}</div>
<div><br>
</div>
<div>Place the compiled A.class at the hard-coded location "/data/tmp/testleak/lib/", then run the main method with JVM flags "-Xlog:class+unload". Observe that no class is unloaded, which is not the case if the main() code runs twice, or if the snippet is
 executed e.g. with Java 17.<br>
</div>
<div><br>
</div>
<div>It seems each time ForkJoinPool creates a new ForkJoinWorkerThread from a loaded class, it is no longer possible to GC the class loader of the class using ForkJoinPool.</div>
<div><br>
</div>
<div>
<div>The leak occurs after this commit, with Java 19+:</div>
<div><br>
</div>
<div><a href="https://github.com/openjdk/jdk19u/commit/00e6c63cd12e3f92d0c1d007aab4f74915616ffb" target="_blank">https://github.com/openjdk/jdk19u/commit/00e6c63cd12e3f92d0c1d007aab4f74915616ffb</a></div>
</div>
<div><br>
</div>
<div>Essentially, our application loads and runs user code. The user changes their code, runs their code again - we use a new class loader to run the changed code in the same JVM. We unload the previous class loader, to free up memory and to avoid problems
 with hot swapping code (an old version of a loaded class can cause an error when trying to hot swap the latest loaded version of that class). So the leaked class loader is giving us trouble.<br>
</div>
<div><br>
</div>
<div>What possible workarounds can we use to avoid this problem?</div>
<div><br>
</div>
<div>Best regards and thanks,</div>
<div>Simeon
<div class="x_gmail-yj6qo"></div>
<div class="x_gmail-adL"><br>
<br>
</div>
</div>
</div>
</div>
</div>
</body>
</html>