vim multiple gopls
多个 gopls daemon 导致内存耗尽
问题及现象
在 vim 中打开大项目(比如 kubernetes)时会导致系统内存被耗尽,最终导致整台电脑死机。其根本的原因是 vim 中配置的各种插件(比如 vim-go、coc.nvim、coc-go、ycm等)都会各自启动一个 gopls serve daemon 进程,如下图所示:
每个 gopls serve daemon 进程都会消耗 2-4G 的内存,甚至更多(对于更大的项目),如下图:
对上述现象 go 团队及社区给出的解释可参考。go 团队启动 gopls serve daemon 时会基于lsp 空间,不同 lsp 空间彼此独立以方便启动多个 daemon 来满足特殊场景的测试/开发等,而 lsp 空间最终会根据 TMPDIR 的目录来具体实现,因此可理解为如果两个 gopls 指定 TMPDIR 目录不相同时将会启动两个不同的 gopls serve daemon 进程。而对于 vim-go、coc.nvim/coc-go 等插件其内部获取 TMPDIR 的方式不一样,得到的 TMPDIR 也不一样(可参考),因此会启动多个 gopls serve daemon 进程,最终导致内存耗尽。
解决方案
为了避免 vim 中多个插件开启多个 gopls serve daemon 进程,可通过使多个插件共享同一个 gopls serve daemon 的方式来减少内存消耗。具体有如下方案:
- 方案一:向 go 团队提交 issue,说明原因后由 go 团队最终解决。社区已有人向 go 团队反馈。
- 方案二:可通过修改 coc.nvim,coc-go 等插件获取 TMPDIR 的结果来指定多个插件共享同一个 gopls,但这种方式需要修改插件的代码源码。可参考
- 方案三:修改 coc.nvim,coc-go 的配置文件,这种方式比较方便处理。可参考
修改 coc.nvim 的配置:
官方原始配置,通过在
coc-setting.json
指定language server
:1
2
3
4
5
6
7
8
9
10{
"languageserver": {
"golang": {
"command": "gopls",
"args": ["-remote=auto"],
"rootPatterns": ["go.mod", ".vim/", ".git/", ".hg/"],
"filetypes": ["go"]
}
}
}这个配置文件会有两个问题:
会导致多个插件有多个 gopls sever daemon 进程,即使加上
-remote=auto
多个插件并不会共享同一个 gopls 进程,因为插件使用的 TMPDIR 目录不一样。在开发过程中,使用代码自动补全时会出现多余的重复选项(如图03),这个问题我是后来才发现的,当不添加
languageserver
的配置时不会有重复的选项(如图04)。
我的 coc.nvim/coc-go 的最终配置为
cos-setting.json
:1
2
3
4
5
6
7
8
9
10{
"go.goplsArgs": ["-remote=auto", "-logfile", "/tmp/gopls-cocvim.log"], // 指定 remote=auto, coc.nvim 的 TMPDIR 与 gopls 的在同一个文件夹下
"go.goplsPath": "/home/qingwei/go/bin/gopls" // 指定共享的 gopls 二进制文件,注意:如果只修改 TMPDIR 也不行,必须使用同一个 gopls 二进制文件启动才能共享同一个 serve daemon。
"go.goplsOptions": {
// go 多个module 嵌套问题:
// https://github.com/golang/tools/blob/master/gopls/doc/workspace.md
// https://github.com/josa42/coc-go
"experimentalWorkspaceModule": true
}
}最终效果如下: