Repository navigation
Description
Description
Details
Summary
When OpenCode spawns subagents (via the task() tool), each subagent that invokes LSP tools (e.g., lsp_diagnostics, lsp_goto_definition) may start a new JDTLS (Eclipse JDT Language Server) process. These JDTLS processes are never terminated after the subagent session completes, leading to significant memory accumulation. In a single coding session, I observed 3 orphaned JDTLS instances consuming ~2.2 GB of physical memory combined, with 6 stale temp data directories (~290 MB on disk).
Environment
OS: macOS (Apple Silicon)
OpenCode binary:
~/.opencode/bin/opencode(113 MB, dated 2025-02-25)JDTLS location:
~/.local/share/opencode/bin/jdtls/(Eclipse Equinox Launcher 1.7.100.v20251111-0406)Java:
/usr/bin/java(JDK 17)Project type: Spring Boot 3.x + MyBatis-Plus (multi-module Maven project, ~60+ source files in service layer)
Reproduction Steps
Open a Java project with OpenCode
Perform a task that triggers subagent delegation — for example, ask the agent to implement a feature that involves:
exploresubagents for codebase pattern discoverytask(category="quick", ...)subagents for code changes
The main agent and each subagent may call LSP tools (
lsp_diagnostics,lsp_goto_definition,lsp_find_references, etc.)After subagents complete and return results, observe that their JDTLS processes remain running
Observed Behavior
Orphaned JDTLS Processes
After a ~1 hour session with 3 subagent delegations, ps shows 3 JDTLS instances still running:
PID PPID STARTED COMMAND
7579 6839 Wed Feb 25 15:09:06 2026 /usr/bin/java -jar .../jdtls/plugins/org.eclipse.equinox.launcher_1.7.100.v20251111-0406.jar ...
8550 6839 Wed Feb 25 15:11:53 2026 /usr/bin/java -jar .../jdtls/plugins/org.eclipse.equinox.launcher_1.7.100.v20251111-0406.jar ...
10869 6839 Wed Feb 25 15:30:05 2026 /usr/bin/java -jar .../jdtls/plugins/org.eclipse.equinox.launcher_1.7.100.v20251111-0406.jar ...
All have PPID 6839 which is the main opencode process.
Actual Memory Usage (per footprint / Activity Monitor)
The ps aux RSS column dramatically underreports JVM memory. The real physical memory (as reported by macOS footprint command and consistent with Activity Monitor) is:
| PID | Physical Memory | Peak | Uptime |
|---|---|---|---|
| 7579 | 751 MB | 778 MB | 57 min |
| 8550 | 708 MB | 733 MB | 54 min |
| 10869 | 787 MB | 810 MB | 36 min |
| Total | ~2.2 GB |
The majority of memory is in VM_ALLOCATE (JVM heap) — e.g., 688 MB / 651 MB / 723 MB respectively.
Full footprint output for PID 7579
java [7579]: 64-bit Footprint: 751 MB (16384 bytes per page)
Dirty Clean Reclaimable Regions Category
688 MB 0 B 0 B 1140 untagged ("VM_ALLOCATE")
20 MB 0 B 0 B 1 MALLOC_NANO
14 MB 0 B 0 B 9 MALLOC_SMALL
11 MB 2432 KB 0 B 20 mapped file
5360 KB 0 B 0 B 14 MALLOC_TINY
3664 KB 0 B 0 B 8 MALLOC_MEDIUM
2512 KB 0 B 0 B 104 stack
751 MB 6592 KB 0 B 3644 TOTAL
</details>
Stale Temp Data Directories
Each JDTLS instance creates a unique data directory in $TMPDIR that is also never cleaned up:
77M /tmp/opencode-jdtls-data2F1raj
40M /tmp/opencode-jdtls-data2L50Y4
40M /tmp/opencode-jdtls-data9kaBOp
47M /tmp/opencode-jdtls-dataMYoWmS
44M /tmp/opencode-jdtls-dataTjKBr1
44M /tmp/opencode-jdtls-datafK1H27
Note: 6 temp directories exist but only 3 processes are running — suggesting some processes were killed at some point but their data directories were not cleaned up. Combined disk waste: ~292 MB.
Expected Behavior
Process lifecycle: When a subagent session ends (task completes or is cancelled), the JDTLS process it spawned should be terminated (SIGTERM → SIGKILL after timeout).
Singleton or pool: Ideally, there should be at most one JDTLS instance per workspace shared across the main agent and all subagents, rather than spawning a new instance per subagent.
Session cleanup: When the main OpenCode session ends (user quits), all child JDTLS processes should be terminated and temp directories should be removed.
Temp directory cleanup:
opencode-jdtls-data*directories should be cleaned up when their associated JDTLS process exits.
Impact
Memory: Each JDTLS instance consumes 700-800 MB of physical memory. A session with multiple subagent delegations can easily waste 2-4 GB of RAM.
Disk: Stale temp directories accumulate at 40-80 MB each.
Scalability: Java projects with large dependency trees (this project has 200+ JARs on the classpath) cause JDTLS to use even more memory for indexing. Users with constrained memory (e.g., 16 GB MacBooks) will hit performance issues quickly.
User confusion: Users see multiple
javaprocesses in Activity Monitor consuming hundreds of MB each, with no obvious way to identify or manage them.
Suggested Fix
Short-term: Add shutdown hooks / process cleanup when subagent sessions end. Send SIGTERM to the JDTLS process associated with that session. Add a cleanup routine on OpenCode exit to kill all child JDTLS processes and remove
opencode-jdtls-data*temp directories.Long-term: Share a single JDTLS instance across the main agent and all subagents within the same workspace. JDTLS supports concurrent requests via LSP protocol — there's no technical reason to spawn separate instances. This would reduce memory from
N × 750 MBto1 × 750 MBregardless of subagent count.JVM tuning (complementary): If multiple instances are unavoidable, consider setting
-Xmx256mor similar JVM heap limits for JDTLS, as the default allows unbounded growth.
Workaround
Users can manually kill orphaned JDTLS processes:
# Kill all OpenCode JDTLS processespkill -f "opencode-jdtls"# Clean up stale temp directoriesrm -rf /tmp/opencode-jdtls-data* $TMPDIR/opencode-jdtls-data*Plugins
No response
OpenCode version
No response
Steps to reproduce
No response
Screenshot and/or share link
No response
Operating System
No response
Terminal
No response
Activity
ivantimdev commentedon Mar 22, 2026
+1 — reproducing on macOS (Apple Silicon)
I’m consistently hitting this with OpenCode on a Java project.
At the moment, I have 2 orphaned JDTLS processes showing as “Not Responding” in Activity Monitor:
Combined, that is about 3.2 GB of physical memory from just two stuck instances.
Both processes are unresponsive, and macOS reports them as “Not Responding.” This happens regularly during normal coding sessions. I do not need to explicitly use subagent delegation to trigger it — routine LSP tool invocations seem to be enough for these processes to accumulate over time.
Environment:
Possible root cause
From a quick look at the code, it seems the issue may be that
Instance.state()inlsp/index.tsonly runs its cleanup callback when theInstanceis destroyed, not when a subagent session ends.As a result, each subagent can trigger
getClients()→schedule(), which may spawn a new JDTLS process, and those processes can accumulate instead of being cleaned up.Possible fix ideas
Track temp data directories on
LSPServer.HandletempDirs?: string[]spawn()with the-datapathclient.shutdown()viafs.rm()Add a
process.on("exit")safety net inlsp/index.tsLonger term, make LSP state truly global rather than per-
InstanceThe dedup logic in
getClients()(s.spawning) already seems to handle concurrent requests within a single state scope. The issue appears to be that subagents may be operating across separate state scopes.@thdxr Would a PR for this be welcome? I’d be happy to put together a fix if this matches the intended architecture.
This is becoming pretty painful in practice, because it can quietly consume several gigabytes of memory during normal work until I manually clean up the stuck JDTLS processes (sometimes system crashes if you forget to do it).
thdxr commentedon Apr 2, 2026
Closing in favor of #20695 where we're collecting all memory-related problems. Please follow that issue for updates.