lsp 2025-07-29

anyone else seen increased cpu usage with clojure-lsp when rebasing etc?

I recall a while back clojure-lsp learned how to rescan the project when you e.g. switch branches.

For a while now (maybe a few weeks or so) what I've seen is that I'll do some trivial change (e.g. squash a bunch of wip commits into a single commit) and I'll notice my cpu usage goes through the roof as clojure-lsp starts to calculate something. I've tried to wait a while but usually I just close my project in emacs, which kill the lsp process.

I was wondering if anyone else has seen anything like this or if there's some diagnostics I could provide to debug this

I managed to look at the logs while this was happening at it was just thousands of lines of

025-08-04T08:53:14.622Z  INFO [clojure-lsp.feature.diagnostics.built-in:301] - :internal/built-in-linters.different-aliases 0ms
2025-08-04T08:53:14.622Z  INFO [clojure-lsp.feature.diagnostics.built-in:301] - :internal/built-in-linters.different-aliases 0ms
2025-08-04T08:53:14.622Z  INFO [clojure-lsp.feature.diagnostics.built-in:304] - :internal/built-in-linters.cyclic-dependencies 0ms
2025-08-04T08:53:14.624Z  INFO [clojure-lsp.feature.diagnostics.built-in:295] - :internal/built-in-linters.find-linter-ignore-comments 26ms
2025-08-04T08:53:14.632Z  INFO [clojure-lsp.feature.diagnostics.built-in:289] - :internal/built-in-linters 5482ms
2025-08-04T08:53:14.632Z  INFO [clojure-lsp.feature.diagnostics.built-in:304] - :internal/built-in-linters.cyclic-dependencies 0ms
2025-08-04T08:53:14.636Z  INFO [clojure-lsp.feature.diagnostics.built-in:298] - :internal/built-in-linters.unused-public-vars 2441ms
2025-08-04T08:53:14.638Z  INFO [clojure-lsp.feature.diagnostics.built-in:298] - :internal/built-in-linters.unused-public-vars 683ms
2025-08-04T08:53:14.639Z  INFO [clojure-lsp.feature.diagnostics.built-in:289] - :internal/built-in-linters 2479ms
2025-08-04T08:53:14.642Z  INFO [clojure-lsp.feature.diagnostics.built-in:289] - :internal/built-in-linters 705ms
2025-08-04T08:53:14.634Z  INFO [clojure-lsp.dep-graph:280] - :internal/maintain-dep-graph 2ms
2025-08-04T08:53:14.643Z  INFO [clojure-lsp.server:613] - :internal/analyze-file 25661ms
2025-08-04T08:53:14.643Z  INFO [clojure-lsp.server:195] - :lsp/refresh-test-tree 0ms
2025-08-04T08:53:14.645Z  INFO [clojure-lsp.feature.file-management:263] - :internal/uri-analyzed-by-clj-depend 0ms
2025-08-04T08:53:14.649Z  INFO [clojure-lsp.dep-graph:280] - :internal/maintain-dep-graph 9ms
2025-08-04T08:53:14.652Z  INFO [clojure-lsp.feature.diagnostics.built-in:298] - :internal/built-in-linters.unused-public-vars 3132ms
2025-08-04T08:53:14.659Z  INFO [clojure-lsp.feature.diagnostics.built-in:289] - :internal/built-in-linters 3214ms
2025-08-04T08:53:14.661Z  INFO [clojure-lsp.feature.diagnostics.built-in:295] - :internal/built-in-linters.find-linter-ignore-comments 42ms
2025-08-04T08:53:14.666Z  INFO [clojure-lsp.dep-graph:280] - :internal/maintain-dep-graph 6ms
2025-08-04T08:53:14.669Z  INFO [clojure-lsp.feature.diagnostics.built-in:304] - :internal/built-in-linters.cyclic-dependencies 0ms
2025-08-04T08:53:14.668Z  INFO [clojure-lsp.server:132] - :lsp/publish-diagnostics 0ms
2025-08-04T08:53:14.672Z  INFO [clojure-lsp.feature.diagnostics.built-in:304] - :internal/built-in-linters.cyclic-dependencies 0ms
2025-08-04T08:53:14.672Z  INFO [clojure-lsp.feature.diagnostics.built-in:301] - :internal/built-in-linters.different-aliases 0ms
2025-08-04T08:53:14.674Z  INFO [clojure-lsp.feature.diagnostics.built-in:295] - :internal/built-in-linters.find-linter-ignore-comments 2ms
2025-08-04T08:53:14.674Z  INFO [clojure-lsp.feature.diagnostics.built-in:301] - :internal/built-in-linters.different-aliases 0ms
2025-08-04T08:53:14.677Z  INFO [clojure-lsp.feature.diagnostics.built-in:304] - :internal/built-in-linters.cyclic-dependencies 0ms
2025-08-04T08:53:14.677Z  INFO [clojure-lsp.feature.diagnostics.built-in:295] - :internal/built-in-linters.find-linter-ignore-comments 56ms
2025-08-04T08:53:14.678Z  INFO [clojure-lsp.feature.diagnostics.built-in:289] - :internal/built-in-linters 2397ms
2025-08-04T08:53:14.681Z  INFO [clojure-lsp.feature.diagnostics.built-in:295] - :internal/built-in-linters.find-linter-ignore-comments 8ms
2025-08-04T08:53:14.681Z  INFO [clojure-lsp.feature.diagnostics.built-in:301] - :internal/built-in-linters.different-aliases 0ms
2025-08-04T08:53:14.683Z  INFO [clojure-lsp.feature.diagnostics.built-in:295] - :internal/built-in-linters.find-linter-ignore-comments 11ms
2025-08-04T08:53:14.683Z  INFO [clojure-lsp.kondo:174] - :internal/kondo-findings->analysis 0ms
2025-08-04T08:53:14.684Z  INFO [clojure-lsp.feature.diagnostics.built-in:298] - :internal/built-in-linters.unused-public-vars 799ms
2025-08-04T08:53:14.694Z  INFO [clojure-lsp.feature.diagnostics.built-in:289] - :internal/built-in-linters 902ms
2025-08-04T08:53:14.694Z  INFO [clojure-lsp.feature.diagnostics.built-in:295] - :internal/built-in-linters.find-linter-ignore-comments 778ms
2025-08-04T08:53:14.697Z  INFO [clojure-lsp.dep-graph:280] - :internal/maintain-dep-graph 3ms
2025-08-04T08:53:14.698Z  INFO [clojure-lsp.dep-graph:280] - :internal/maintain-dep-graph 14ms
2025-08-04T08:53:14.685Z  INFO [clojure-lsp.feature.diagnostics.built-in:295] - :internal/built-in-linters.find-linter-ignore-comments 69ms
2025-08-04T08:53:14.704Z  INFO [clojure-lsp.feature.diagnostics.built-in:298] - :internal/built-in-linters.unused-public-vars 3211ms
2025-08-04T08:53:14.720Z  INFO [clojure-lsp.feature.diagnostics.built-in:301] - :internal/built-in-linters.different-aliases 0ms
2025-08-04T08:53:14.720Z  INFO [clojure-lsp.feature.diagnostics.built-in:304] - :internal/built-in-linters.cyclic-dependencies 0ms
2025-08-04T08:53:14.723Z  INFO [clojure-lsp.feature.diagnostics.built-in:289] - :internal/built-in-linters 3251ms
2025-08-04T08:53:14.724Z  INFO [clojure-lsp.feature.diagnostics.built-in:295] - :internal/built-in-linters.find-linter-ignore-comments 120ms
2025-08-04T08:53:14.724Z  INFO [clojure-lsp.feature.diagnostics.built-in:304] - :internal/built-in-linters.cyclic-dependencies 0ms
2025-08-04T08:53:14.725Z  INFO [clojure-lsp.dep-graph:280] - :internal/maintain-dep-graph 1ms
2025-08-04T08:53:14.725Z  INFO [clojure-lsp.feature.file-management:153] - :reference-files/find 77ms
2025-08-04T08:53:14.725Z  INFO [clojure-lsp.feature.file-management:151] - :internal/notify-references 78ms
2025-08-04T08:53:14.727Z  INFO [clojure-lsp.feature.diagnostics.built-in:295] - :internal/built-in-linters.find-linter-ignore-comments 7ms
2025-08-04T08:53:14.731Z  INFO [clojure-lsp.dep-graph:280] - :internal/maintain-dep-graph 53ms
2025-08-04T08:53:14.731Z  INFO [clojure-lsp.feature.diagnostics.built-in:298] - :internal/built-in-linters.unused-public-vars 3271ms
2025-08-04T08:53:14.730Z  INFO [clojure-lsp.feature.diagnostics.built-in:301] - :internal/built-in-linters.different-aliases 0ms
2025-08-04T08:53:14.736Z  INFO [clojure-lsp.feature.diagnostics.built-in:298] - :internal/built-in-linters.unused-public-vars 1667ms
2025-08-04T08:53:14.731Z  INFO [clojure-lsp.feature.diagnostics.built-in:298] - :internal/built-in-linters.unused-public-vars 1912ms
2025-08-04T08:53:14.741Z  WARN [clojure-lsp.kondo:372] - [clj-kondo] No configs copied.
2025-08-04T08:53:14.743Z  INFO [clojure-lsp.feature.diagnostics.built-in:295] - :internal/built-in-linters.find-linter-ignore-comments 16ms
2025-08-04T08:53:14.749Z  INFO [clojure-lsp.feature.diagnostics.built-in:304] - :internal/built-in-linters.cyclic-dependencies 0ms
2025-08-04T08:53:14.750Z  INFO [clojure-lsp.feature.diagnostics.built-in:298] - :internal/built-in-linters.unused-public-vars 3356ms
2025-08-04T08:53:14.751Z  INFO [clojure-lsp.feature.diagnostics.built-in:289] - :internal/built-in-linters 4127ms
2025-08-04T08:53:14.753Z  INFO [clojure-lsp.feature.diagnostics.built-in:289] - :internal/built-in-linters 1944ms
2025-08-04T08:53:14.753Z  INFO [clojure-lsp.dep-graph:280] - :internal/maintain-dep-graph 2ms
2025-08-04T08:53:14.755Z  INFO [clojure-lsp.feature.diagnostics.built-in:301] - :internal/built-in-linters.different-aliases 0ms
2025-08-04T08:53:14.756Z  INFO [clojure-lsp.kondo:174] - :internal/kondo-findings->analysis 0ms
2025-08-04T08:53:14.756Z  INFO [clojure-lsp.feature.file-management:259] - :internal/uri-analyzed-by-clj-kondo 112ms
2025-08-04T08:53:14.759Z  INFO [clojure-lsp.dep-graph:280] - :internal/maintain-dep-graph 0ms
2025-08-04T08:53:14.767Z  INFO [clojure-lsp.feature.diagnostics.built-in:304] - :internal/built-in-linters.cyclic-dependencies 0ms
2025-08-04T08:53:14.770Z  INFO [clojure-lsp.feature.diagnostics.built-in:289] - :internal/built-in-linters 3409ms
2025-08-04T08:53:14.771Z  INFO [clojure-lsp.feature.diagnostics.built-in:301] - :internal/built-in-linters.different-aliases 0ms
2025-08-04T08:53:14.773Z  INFO [clojure-lsp.feature.diagnostics.built-in:295] - :internal/built-in-linters.find-linter-ignore-comments 11ms
2025-08-04T08:53:14.774Z  INFO [clojure-lsp.feature.diagnostics.built-in:289] - :internal/built-in-linters 1761ms
2025-08-04T08:53:14.780Z  INFO [clojure-lsp.dep-graph:280] - :internal/maintain-dep-graph 28ms

I have recently switched to Neovim from VSCode, mainly to avoid the lagginess that seems to plague VSCode. However, I can't seem to avoid the clojure-lsp lag - it's even noticeably slower (or perhaps I just see it better) - I also get an occasional time out. However, I don't see a CPU spike or anything like that that would explain the error. Any similar issues by neovim users?

I've been using Neovim (AstroNvim) with Clojure LSP & Conjure for the last couple of years, on two different commercial projects. I haven't experienced any performance issues with Clojure LSP or Neovim's LSP client (Neovim 0.9 / 0.10 /0.11). I do have 32Gb on Linux / MacOSX machines. Note: I've also run Clojure LSP on an android tablet via Termux and the jar version for Clojure LSP and it worked well.

šŸŽ‰ 2

have you tried the latest nightly build? there might be an improvement in there

I was just reading through that. Indeed, clojure-lsp is the most memory intensive program in my list right now!

how much memory does your system have

I have an M1 Macbook Air with 16GB.

I have one too (one of my systems)

Right now I see clojure-lsp at 6.53 GB

I will check how to install nightly builds with use in Neovim

6gb sounds too much. About the lag, I never heard issues like that, after started server is fast. Maybe it's a nvim thing like emacs has lsp-idle-time which is the time to process server new messages

I did a fresh restart of everything incl. download the nightly build mentioned above. So far everything is very very fast, but I will check to see how it goes through the course of the day.

šŸ‘ 2

After a day of working, I see the memory usage at 3.85gb

The initial memory at startup was around 2gb

I have these java processes which run clojure-lsp. the 7gb one is metabase, one of the biggest OSS clojure projects I know

I don't think it can be clj-kondo anymore though since there's nothing being kept in memory anymore (hopefully) after run! is finished

-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
ClojureScript                  305           6192           2237          69649
Clojure                        358           9626           3965          67046
ClojureC                        21            340            168           2026

looks like a pretty normal project

Yep, nothing huge.

it could be that clojure-lsp is also using a cache (using memoize or other) with collections as keys. this is what messed up clj-kondo's memory usage

The interesting thing is that all that memory is swapped to disk. The "real memory" used is only 40MB.

ah, it could be that it's just keeping files in the memory cache, the OS does that to keep things fast

no, maybe your machine is just swapping because of memory shortage

Yes, it's definitely swapping out.

is memory pressure yellow in your activity tab?

Does it even have any other color? šŸ˜„

hehe. mine is also constantly yellow (on my m1). I opted for more memory in my newer machine

is it still slow though? or managable?

neovim became usable again. I've also killed an ollama server I had running in the background. AI so far has been only a drag šŸ˜„

The LSP is snappy - it feels like I'm not waiting on it at all.

if it feels fast, that's the most important thing I guess

if lsp has a memory leak, should manifest itself after using it for a longer time and running larger and larger. should be detectable via an hprof file

I'll clock out for the day and resume tomorrow, let's see how another full day feels like.

oh I guess you should compile the native image with --enable-monitoring=heapdump,jvmstat to be able to get an hprof file

might as well run a jvm when testing for this

to be honest I'm too close to my vacation (Friday, yay!) to test for this. It feels like it could be tested synthetically somehow by running a server and sending 10 commands per second for a period of a hew hours...

šŸ‘ 1

Ollama serve affects a lot my machine memory BTW. And yes, clojure-lsp has a atom in memory for all analysis, but I don't think it should be a problem unless it's a metabase project with thousands of analysis

You can always tune and disable keywords or other features analysis although not ideal

Speaking of, in Calva/VSCode, I could get references to keywords etc, but in neovim it seems not. I guess Calva overrides some configuration properties? I will have to revisit.

Sounds like you are using a outdated clojure-lsp? Keyword References should work all the same for any client

In the end it's just LSP references feature

I’m using the nightly version as of this morning

What I used to able to do, was in big EDN file, I was able to go to definition on plain keywords and it would take me to the place where that keyword appears as a key. Not sure if that was a VACode heuristic though.

maybe, clojure-lsp supports find-definition of keywords only when it's a reg-fx or similar, not only map keys

is it possible to constrain lsp-find-references to a specific set of namespaces somehow?

pretty sure the path regex stuff isn't working as described, @ericdallo šŸ˜…

should i file an issue?

why do you use multiple of the same strings on source-paths-ignore-regex and why do you use a trailing /?

(not that is probably matters)

i don't:

{:clean                     {:automatically-after-ns-refactor true
                             :ns-inner-blocks-indentation     :same-line
                             :ns-import-classes-indentation   :same-line}
 :cljfmt-config-path        ".cljfmt.edn"
 :source-paths-ignore-regex ["src/cognician/chat/"
                             "src/cognician/c3_model"]
 :paths-ignore-regex        ["src/cognician/chat/"
                             "src/cognician/c3_model"]}

and, the trailing slash is so that chat_c4 is not filtered out

ok, I see something different in your screenshot

yeah the duplicates are in the lsp-clojure-server-info output, but not in the config i maintain

I'm using this script to run clojure-lsp locally:

#!/usr/bin/env bash

# override project config:
export CLJ_KONDO_EXTRA_CONFIG_DIR=~/.config/clj-kondo

clj -Sdeps '{:aliases
              {:lsp
                {:replace-paths []
                 :replace-deps
                  {org.clojure/clojure {:mvn/version "1.11.1"}
                   clj-kondo/clj-kondo {:local/root "/Users/borkdude/dev/clj-kondo"}
                   clojure-lsp/clojure-lsp {:local/root "/Users/borkdude/dev/clojure-lsp/cli"}
                   cider/cider-nrepl {:mvn/version "0.28.6"}}}}}' \
                       -M:lsp -m clojure-lsp.main "$@"
I use this to always have my dev version of clj-kondo active, but you could also try this to debug the source-paths regex thing

usually i'd just tough it out (i have been for years), but in this situ i'd really value the ability to filter like this

ok ty!

in your case you can leave out clj-kondo and just let clojure-lsp pick clj-kondo

I don't remember why I have the env var at the top though

but you get the gist

i do, thanks borkers!

currently not

210,000 loc monorepo šŸ˜„

reworking a part of the code some times involves working through a set of find-references. being able to limit that to a src sub-path reduces friction

you can configure clojure-lsp to ignore some paths for analysis, then it should exclude all features including references

that might help, thank you!

:paths-ignore-regex or :source-paths-ignore-regex https://clojure-lsp.io/settings/#all-settings

šŸ™ 1

i have two paths src/cognician/chat/* and src/cognician/chat_c4/*. i want the first one ignored but not the second. my settings, which don't seem to work after a restart of emacs (in .lsp/config.edn; .lsp is a sibling of src): :source-paths-ignore-regex ["src/cognician/chat/.*"] i guess i need to clear caches šŸ˜„

trying that now...

naw it's still listing stuff from that folder

the source-paths is kinda of tricky, try checking clojure-lsp server logs about the source-paths using when starting

šŸ™ 1