Skip to content

Conversation

Copilot
Copy link

@Copilot Copilot AI commented Oct 2, 2025

Problem

The LanguageServerWrapper class retained disposed Project instances as keys in the static projectToLanguageServerWrapper map, causing a memory leak as reported in Android Studio's memory profiler:

(root): java.lang.Class(org.wso2.lsp4intellij.client.languageserver.wrapper.LanguageServerWrapper)
(static): java.util.concurrent.ConcurrentHashMap
table: java.util.concurrent.ConcurrentHashMap$Node[]
[]: java.util.concurrent.ConcurrentHashMap$Node
key: com.intellij.openapi.project.impl.ProjectImpl (disposed)

When a project was closed, the Project reference was never removed from static maps, preventing garbage collection and causing memory to leak over time.

Root Cause

  1. Project instances were added to projectToLanguageServerWrapper in the constructor
  2. LanguageServerWrapper instances were added to projectToLanguageWrappers when created
  3. When projects were closed, the projectClosing() listener did not:
    • Stop the language servers
    • Remove the Project from static maps
    • Remove wrapper references from tracking collections

Solution

This PR introduces minimal, surgical changes to ensure proper cleanup:

1. Added LanguageServerWrapper.dispose() method

A dedicated cleanup method that removes the Project from projectToLanguageServerWrapper. This is separate from stop() because:

  • stop() is used for both temporary stops (reconnect/restart) and permanent stops (project closing)
  • Removing the project during stop() would break forProject() lookups after reconnects
  • dispose() is only called when the project is actually being closed

2. Enhanced LSPProjectManagerListener.projectClosing()

Now properly shuts down language servers and cleans up resources:

wrapper.stop(true);      // Shutdown the language server
wrapper.removeWidget();  // Remove UI widget
wrapper.dispose();       // Clean up static map references
IntellijLanguageClient.removeWrapper(wrapper);  // Remove from tracking maps

3. Enhanced IntellijLanguageClient.removeWrapper()

Now removes wrappers from projectToLanguageWrappers map and cleans up empty sets, preventing indirect retention of Project instances through wrapper references.

Impact

  • ✅ Fixes reported memory leak of disposed Project instances
  • ✅ Minimal changes (3 files, 19 lines added)
  • ✅ No breaking changes to existing functionality
  • ✅ Wrapper reuse for reconnect/restart scenarios still works correctly
  • ✅ All static maps are properly cleaned up when projects are disposed

Testing

The fix has been designed to handle both scenarios correctly:

  • Project closing: All references cleaned up, memory can be freed
  • Reconnect/restart: Wrapper instance reused, forProject() still works
Original prompt

This section details on the original issue you should resolve

<issue_title>org.wso2.lsp4intellij.client.languageserver.wrapper.LanguageServerWrapper retains disposed Project</issue_title>
<issue_description>### Description

(root): java.lang.Class(org.wso2.lsp4intellij.client.languageserver.wrapper.LanguageServerWrapper)
(static): java.util.concurrent.ConcurrentHashMap
table: java.util.concurrent.ConcurrentHashMap$Node[]
[]: java.util.concurrent.ConcurrentHashMap$Node
key: com.intellij.openapi.project.impl.ProjectImpl (disposed)

Steps to Reproduce

Memory leak snapshot from Android Studio memory usage reporting

Version

Android Studio Meerkat Feature Drop | 2024.3.2 Canary 3

Environment Details (with versions)

No response</issue_description>

Comments on the Issue (you are @copilot in this section)

Fixes #390

💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@Copilot Copilot AI changed the title [WIP] org.wso2.lsp4intellij.client.languageserver.wrapper.LanguageServerWrapper retains disposed Project Fix memory leak: Remove disposed Project from static maps Oct 2, 2025
Copilot finished work on behalf of NipunaRanasinghe October 2, 2025 11:54
@Copilot Copilot AI requested a review from NipunaRanasinghe October 2, 2025 11:54
@NipunaRanasinghe NipunaRanasinghe marked this pull request as ready for review October 2, 2025 18:50
@Copilot Copilot AI review requested due to automatic review settings October 2, 2025 18:50
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a memory leak where disposed Project instances were retained in static maps within LanguageServerWrapper, preventing proper garbage collection when projects were closed in IntelliJ-based IDEs.

  • Added dispose() method to LanguageServerWrapper for cleanup of static map references
  • Enhanced project closing listener to properly shutdown language servers and cleanup resources
  • Improved wrapper removal to clean up projectToLanguageWrappers map entries

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
LSPProjectManagerListener.java Added proper cleanup sequence when projects are closing
LanguageServerWrapper.java Added dispose method to remove project references from static maps
IntellijLanguageClient.java Enhanced removeWrapper to clean up projectToLanguageWrappers map

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

org.wso2.lsp4intellij.client.languageserver.wrapper.LanguageServerWrapper retains disposed Project

3 participants