Skip to content

SONARJAVA-6972 fix: clear safeSymbols to prevent memory leak - #6142

Merged
romainbrenguier merged 2 commits into
masterfrom
romain/fix-opt-map-leak
Sep 22, 2026
Merged

romainbrenguier merged 2 commits into
masterfrom
romain/fix-opt-map-leak

Conversation

@romainbrenguier

Copy link
Copy Markdown
Contributor

Problem

The safeSymbols field in BoxedBooleanExpressionsCheck accumulates symbols across file scans but is never cleared. This causes a memory leak when the analyzer processes multiple files, as the set continues to grow without bound.

The two static caches (ifStatementCache and firstNullCheckCache) are properly cleared at the start of each file scan, but safeSymbols was overlooked.

Solution

Add safeSymbols.clear() in scanFile() alongside the existing cache clear calls. This ensures the set is reset for each file while maintaining correct behavior within a single file.

Verification

  • Existing tests pass
  • The fix follows the same pattern used for the other caches in the same method

@hashicorp-vault-sonar-prod hashicorp-vault-sonar-prod Bot changed the title fix: clear safeSymbols to prevent memory leak SONARJAVA-6972 fix: clear safeSymbols to prevent memory leak Sep 14, 2026
@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

SONARJAVA-6972

@romainbrenguier
romainbrenguier marked this pull request as ready for review September 14, 2026 15:24
romainbrenguier and others added 2 commits September 15, 2026 11:40
…lived processes

Move cache clearing (ifStatementCache, firstNullCheckCache, safeSymbols)
from the start of scanFile to a finally block, so the last scanned file's
AST is not retained for the lifetime of the plugin classloader in IDE
sessions (SonarLint).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gitar-bot

gitar-bot Bot commented Sep 15, 2026 •

Copy link
Copy Markdown
Code Review ✅ Approved 1 closed / 1 findings

Clears the safeSymbols cache on each file scan to prevent memory leaks in long-lived processes. The fix addresses the issue where symbols accumulated across files, and now follows the same pattern as the existing ifStatementCache and firstNullCheckCache clearing logic.

✅ 1 closed
✅ Performance: Caches cleared only before scan, so last file's AST stays retained

📄 java-checks/src/main/java/org/sonar/java/checks/BoxedBooleanExpressionsCheck.java:81-82 📄 java-checks/src/main/java/org/sonar/java/checks/BoxedBooleanExpressionsCheck.java:90-98 📄 java-checks/src/main/java/org/sonar/java/checks/BoxedBooleanExpressionsCheck.java:225 📄 java-checks/src/main/java/org/sonar/java/checks/BoxedBooleanExpressionsCheck.java:246
The clear calls (including the new safeSymbols.clear()) run at the start of scanFile, so after the final file of the analysis is scanned, ifStatementCache still maps every ancestor Tree of each null-check usage (populated in getParentConditionalBranch, line 246) and firstNullCheckCache still holds that file's Symbols. Because those two maps are static, that data stays strongly reachable for the lifetime of the plugin classloader — it is not released when the check instance and VisitorsBridge are dropped — which keeps a whole compilation unit's tree alive in long-lived processes (SonarLint/IDE sessions), contradicting the PR description's claim that the two static caches are "properly cleared". Clearing at the end of the scan (or via the EndOfAnalysis hook, whose javadoc explicitly warns that "keeping state between files can lead to memory leaks") releases the memory instead of merely bounding it to one file.

Review coverage

🧪 Functional validation No results

📋 Rules No rules evaluated

🤖 Auto-approval Not enabled · Set up

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Counting what did not apply, without listing it.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqube-next

Copy link
Copy Markdown
Contributor

@leveretka

Copy link
Copy Markdown
Contributor

The code looks fine. Did you manage to reproduce the leak and test locally that it doesn't occur anymore?

You can run the analysis locally and connect to the process with VisualVM and get a heap dump before and after the fix. This way you'll be able to not just say that you fixed it but You could also estimate the impact on a real project. (You can try analyzing SonarJava itself at least)

And another question, don't you need an approval from someone from the CQ team? do you think mine would be enough here?

@romainbrenguier

Copy link
Copy Markdown
Contributor Author

@leveretka
On sonar-java at least, it doesn't seem to have an impact on memory used. Do you have an example project where you know the memory leak is visible?

Before the fix:
Screenshot from 2026-09-22 10-06-17

After the fix:
Screenshot from 2026-09-22 09-50-25

@leveretka

Copy link
Copy Markdown
Contributor

@romainbrenguier I'm not sure I can answer the question about the leak based on the charts solely. Of course the retained memory highly depends on the actual usage of Optional.map in the repo.

What I'd check is if 'safeSymbols' is growing till the end of analysis by generating heapdumps during the analysis. And see if it contains symbols from the old files and references old files ASTs. Of course at the end of the analysis everything is cleaned.

Also, I believe it will more affect single huge modules.

Another possible issur is that the source files are still there due to the other leak. But that requires looking at the heapdump between the files.

@romainbrenguier

Copy link
Copy Markdown
Contributor Author

@romainbrenguier I'm not sure I can answer the question about the leak based on the charts solely. Of course the retained memory highly depends on the actual usage of Optional.map in the repo.

What I'd check is if 'safeSymbols' is growing till the end of analysis by generating heapdumps during the analysis. And see if it contains symbols from the old files and references old files ASTs. Of course at the end of the analysis everything is cleaned.

Also, I believe it will more affect single huge modules.

Another possible issur is that the source files are still there due to the other leak. But that requires looking at the heapdump between the files.

Looking at the heap dump during the analizis of sonar-java, the safeSymbols map always have 0 elements, so it's not a good example to test the memory leak.

@romainbrenguier

Copy link
Copy Markdown
Contributor Author

I don't have a good example to reproduce the leak for now, but I'm going to merge anyway since it can only improve things.

@romainbrenguier
romainbrenguier merged commit 9b74ca0 into master Sep 22, 2026
17 checks passed
@romainbrenguier
romainbrenguier deleted the romain/fix-opt-map-leak branch September 22, 2026 15:40
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.

2 participants