-
Notifications
You must be signed in to change notification settings - Fork 132
Description
Describe the bug
On reused javac daemons, such as Gradle, Manifold will exhaust the JVM heap after about ~30-50 uses on large codebases or large dependencies with 4 GiB of RAM allocated.
To Reproduce
Steps to reproduce the behavior:
- A repo with either a rather large code base, or large dependencies may be required.
- The repo needs to be using a reusable compiler daemon, normally involving Gradle.
- With the repo, you'll want to run a
clean compileJavaor equivalent several times. - Find that the JVM has exhausted memory after several tries.
Note: this is easier to reproduce if you hard limit the daemon to very little RAM that is still sufficient to compile the application in the first place.
Expected behavior
The daemon to be infinitely reusable without leaking.
Screenshots
I will note, the 'Retained Heap' is deceptive as it is not accounting for the class loader leaked via this way.
Desktop (please complete the following information):
- OS Type & Version: Arch Linux, Linux 6.17.1-1-cachyos-bore-lto
- Java/JDK version: Azul 21.0.4
- Gradle: 8.14
- IDE version (IntelliJ IDEA or Android Studio): N/A, but: IntelliJ IDEA 2022.2.2
- This is not required to reproduce the bug.
- Manifold version: 2025.1.27
- Manifold IntelliJ plugin version: N/A, but: 2022.2.44
- This is not required to reproduce the bug.
Additional context
Requires a reusable daemon. The daemon in question must remain enabled and, in the case of Gradle, must not use --no-daemon.
Note: messing with environment variables, including injecting _JAVA_OPTIONS or JAVA_TOOL_OPTIONS in the case of Gradle will spawn a new daemon as it'll deem the old one incompatible. This is also true for messing with JVM flags, or explicitly declaring --no-daemon with an environment-incompatible wrapper.
You may wish to...
- Use
env "_JAVA_OPTIONS=-XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError" <your IDE> - Pass it directly via
org.gradle.jvmargs=-XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryErroringradle.properties, or using the-Dargument.
Stack trace
N/A, as relevant exceptions are just thrown indeterminately and not at the actual memleak; but I can share where it is leaking:
### Tree split @ Entry#referent
- at java.lang.ThreadLocal @ 0x727229e00
- at java.lang.ThreadLocal$ThreadLocalMap$Entry#referent
- note: internally a `WeakReference`
### Tree split @ Entry#value
- at java.lang.ThreadLocal @ 0x727229e00
- at manifold.internal.host.JavacManifoldHost#_javaParser
- at manifold.internal.javac.JavaParser#_host
- at java.lang.ThreadLocal$ThreadLocalMap$Entry#value
- note: strongly retained
### Root of the stack
- at java.lang.ThreadLocal$ThreadLocalMap$Entry[128]#101
- at java.lang.ThreadLocal$ThreadLocalMap#table
- at java.lang.Thread#threadLocals
- at Thread[/127.0.0.1:57540 to /127.0.01:38471 workers]