一次 VS Code Remote SSH 资源占用排查:4 核 8G 服务器多了近 1G 内存
前一天,我刚因为 terminalRemoteResolver API 不兼容,把 Remote - SSH 扩展回退到了 0.124.0。连接是恢复了,但远端 CPU 长时间维持在约 20%。这件事放在高性能工作站上不算显眼,换到一台正在跑业务的 4 核 8G 服务器上,感受完全不同。
这次我没有只看任务管理器里一个叫 MainThread 的进程,也没有把“还能连上”当成正常。下面记录的是几轮断开、重连、关闭 AI 功能和执行 Remote-SSH: Kill VS Code Server on Host 后得到的实际数据。
前一篇兼容性问题的经过见:修复 VS Code Remote SSH 的 terminalRemoteResolver API 错误。本文讨论的是连接恢复以后出现的另一类问题。
MainThread 不是进程用途
Linux 进程列表里的 MainThread 只是 Node.js 主线程常见的名称。只看这一列,无法判断它究竟在做什么。VS Code Server 的主进程、扩展宿主和文件监听进程都可能显示成这个名字。
真正有用的是完整参数:
ps -eo pid,ppid,pcpu,pmem,rss,etime,comm,args --sort=-pcpu | head -n 30参数里出现以下内容时,才能把职责分开:
--type=extensionHost:远端扩展宿主;--type=fileWatcher:文件监听;--type=ptyHost:终端宿主;server-main.js:VS Code Server 主进程。
这也符合 VS Code 的工作方式:Remote - SSH 会在远端安装 VS Code Server,多数工作区扩展也在远端运行,而不是全部留在本机。VS Code 官方文档明确写了这一点。
所以,Remote - SSH 不是只有一条 SSH 会话。打开远端目录以后,相当于在服务器上启动了一套 Node.js 服务和扩展运行环境。
我一开始也把工作区判断错了
最初我看到某个 VS Code Server 进程的当前目录是 /root,又看到 Git 在 /root 停止向上查找,于是误以为 VS Code 打开了整个根用户目录。
这个判断不对。进程的当前目录不等于 VS Code 的工作区目录,Git 的提示也只能说明它向上查找仓库时碰到了文件系统边界。
后来我检查了文件监听进程的 inotify 数量,再和实际项目目录数比较:
grep -h '^inotify' /proc/FILE_WATCHER_PID/fdinfo/* | wc -l
find PROJECT_DIR -xdev -type d | wc -l两边都是 19,857。这才证明文件监听器盯的是 PROJECT_DIR,不是整个 /root。
这个项目本身有约 119,357 个文件和 19,857 个目录。即使没有扩展报错,仅维护这些监听也不是零成本。排查时不能靠进程名或 cwd 猜工作区,应该把进程参数、监听数量和目录规模放在一起看。
三种状态下的实际占用
我先完全关闭 VS Code,只保留普通 SSH,再分别测试关闭 AI 功能和启用 AI 功能的情况。下面的内存是相关进程 RSS 相加后的近似值。RSS 会重复计算共享内存页,不能直接等同于系统实际少掉的物理内存,但足以比较同一台机器上的变化。
只运行普通 SSH
VS Code 窗口完全关闭后,.vscode-server 相关进程合计约 56.9 MiB。这些主要是少量历史 agent host 和命令 shell,连续采样时 CPU 都是 0%。
此时系统可用内存约 3.1~3.2 GiB。
连接 VS Code,关闭 AI 功能
重新连接同一个项目后,VS Code Server 相关 RSS 合计约 752 MiB:
- Server 主进程约
151.5 MiB; - Extension Host 约
272 MiB; - Pty Host 约
72.4 MiB; - File Watcher 约
54.6 MiB; - Dockerfile 与 Compose 语言服务合计约
121 MiB。
这次十秒采样中,Extension Host 平均占用单个逻辑 CPU 的 7.7%,一秒峰值达到 28%。当时安装 0.124.0 后,我在界面和系统监控里看到的则是长时间约 20%。两组数字不是同一次测量,也不能据此断言所有 0.124.0 都有同样问题。
Linux 常见进程 CPU 百分比按一个逻辑 CPU 计算。在 4 核机器上,单进程 20% 相当于持续使用 0.2 个逻辑 CPU,约占整机理论计算能力的 5%。它不会单独打满服务器,但持续占用和瞬时峰值会侵蚀业务余量。
启用 AI 功能时
问题更明显的一次采样里,VS Code Server 相关 RSS 合计约 1.27 GiB:
- Extension Host 约
428 MiB; - Copilot CLI 约
245 MiB; - Agent Host 约
126 MiB; - File Watcher 约
105 MiB; - Server 主进程约
136 MiB。
Copilot Chat 日志还出现了真实错误:
Failed to create database. Falling back to in-memory db: Error: unable to open database file紧接着日志记录了工作区分块搜索和嵌入模型加载。数据库无法打开后退回内存,确实会改变资源使用方式;但现有日志不能证明它就是全部 CPU 峰值的唯一原因,也没有证据表明它一直在无限重试。
只在界面里关闭 AI 功能后,新启动的 Extension Host 内存下降了,但旧的 Agent Host 和 Copilot CLI 仍然留在远端。执行 Remote-SSH: Kill VS Code Server on Host 再重连后,它们才从当前会话中消失,相关 RSS 回落到约 744~752 MiB。VS Code 的远程开发故障排查文档也把这个命令列为清理远端 Server、处理多类连接问题的通用步骤。
服务器有 swap,不代表这 1G 不重要
这台服务器有两层交换空间:
/swapfile 3.9G used 159.5M priority 99
/dev/zram0 3.5G used 3.0G priority 100swapon --show 里的 zram USED 是压缩前的逻辑数据量,不是它实际占用的物理内存。后来复查 zramctl,数据是这样的:
NAME DISKSIZE DATA COMPR TOTAL ALGORITHM
/dev/zram0 3.5G 3G 961.2M 1013.9M lzo-rle这里的 DISKSIZE 3.5G 是最大逻辑容量,DATA 3G 是送进 zram 前的原始页大小。真正压缩后的数据是 COMPR 961.2M,算上分配器和元数据以后,TOTAL 1013.9M 才是 zram 当前消耗的物理内存。
因此,swapon --show 显示的 USED 3G 和实际占用约 1014 MiB 并不冲突。前者回答“有多少原始内存页被换进来”,后者回答“为了保存这些压缩页花了多少物理内存”。free -h 里的 Swap 已用约 3.1G,是 zram 的逻辑 3G 加上磁盘 swapfile 的 159.5M,也不能当成真实物理内存消耗。
也就是说,zram 不是凭空增加了 3.5G 内存。它已经占着约 1G 物理内存,读写时还需要 CPU 做压缩和解压。采样时系统仍有 94%~98% 空闲 CPU,也没有持续 swap-in 或 swap-out,因此当时并不存在 CPU 打满或交换风暴。真正的问题是余量:业务高峰到来时,额外的 700M 到 1G 常驻进程会更早触发回收、压缩甚至 OOM。
.vscode-server 已经占了 3.5G 磁盘
内存之外,我还检查了远端目录:
du -sxh ~/.vscode-server
du -x -h --max-depth=1 ~/.vscode-server | sort -h
du -x -h --max-depth=2 ~/.vscode-server/cli | sort -h | tail结果是 .vscode-server 共约 3.5 GiB,其中 cli 目录就有 3.3 GiB。里面保留了五套完整的 Stable Server,每套约 569~699 MiB,顶层还有五个约 32~34 MiB 的 code-* 可执行文件。
服务器根分区只有 40G,当时已使用 23G。一个远程编辑器目录占用了已用空间的大约 15%,这个规模不能再当缓存碎屑忽略。
我没有直接给出 rm -rf ~/.vscode-server/* 之类的命令。多个 VS Code 版本可能对应仍在运行或随后还会使用的 Server,盲删会中断会话,也可能导致下次连接重新下载。先用官方 Kill 命令结束当前 Server,再核对正在运行的版本和各目录大小,最后决定是否清理旧版本,风险更可控。
删除旧目录以后再测一次
我后来还是直接删除了原来巨大的 ~/.vscode-server/,随后重新连接,让 VS Code 安装当前版本。这是已经发生的操作,不是给读者的清理命令。删除时仍有旧进程存活,结果也说明为什么不应该只看 du。
新目录从约 3.5 GiB 降到了 729 MiB,其中当前 Stable Server 占 687 MiB,扩展目录 6.9 MiB,数据与日志约 2.9 MiB。根分区的已用空间从约 23G 降到 20G,使用率由 61% 变成 54%,可用空间从约 15G 增加到 18G。
磁盘并没有完全释放。lsof +L1 找到六个存活了十几天到近八十天的历史 agent host。它们仍然打开着九个已经删除的唯一文件,合计约 156 MiB:
可见的 .vscode-server 目录 729 MiB
已删除但仍被进程打开的文件 156 MiB
当前实际占用 约 885 MiB只有相关进程退出后,这 156 MiB 才会真正归还文件系统。删除目录不会让已经装入内存的进程自动消失。
内存没有跟着磁盘一起降下来
这次重连后,当前会话的八个进程合计约 669.5 MiB RSS,六个历史 Agent 另占 33.1 MiB RSS,总计约 702.6 MiB RSS。主要部分是:
- Extension Host:
218 MiB; - Server Main:
140 MiB; - File Watcher:
107 MiB; - Pty Host:
78 MiB; - Compose 与 Dockerfile 语言服务合计约
125 MiB; - 当前的
code-*command shell 约16.9 MiB。
这和进程监控截图能对上。几个 code-* 只有几兆到十几兆,真正占内存的是显示为 MainThread 的 Node.js 进程。名称相同不代表用途相同,仍然要看完整参数里的 extensionHost、fileWatcher、ptyHost 和 server-main.js。
系统当时已用物理内存约 4.0 GiB,可用约 2.8 GiB。这个数字已经包含 zram 实际占用的约 1014 MiB,不能再把它加一次。删除历史 Server 主要收回了磁盘,当前会话该用的内存仍然在。
十秒 CPU 采样里,Extension Host 平均占单个逻辑 CPU 的 5.89%,两次一秒峰值分别达到 22.55% 和 23%。Server Main 平均约 0.1%,其他进程在这段采样里接近零。这一轮不是持续 20%,但“MainThread 动不动跳到 20% 以上”确实测到了。
以后我会怎样连接业务服务器
在 4 核 8G 机器上,我现在把 Remote - SSH 当作一个会部署到远端的开发服务,而不是普通终端的替代品。连接前后至少会留一组基线:
free -h
swapon --show
zramctl
ps -eo pid,ppid,pcpu,pmem,rss,etime,comm,args --sort=-pcpu | head -n 30
du -sxh ~/.vscode-server如果 Extension Host 持续占用 CPU,再用 pidstat 连续采样,而不是只截一秒:
pidstat -u -r -p EXTENSION_HOST_PID 1 10不需要远端 AI 时就关闭它;关闭后仍有 Agent Host 或 CLI 残留,就执行 Remote-SSH: Kill VS Code Server on Host,然后重新连接验证。项目目录很大时,还要检查究竟启用了哪些远端扩展,以及文件监听是否覆盖了本不需要的生成目录。
真正让我警惕的是这些开销叠加起来的结果:Remote - SSH 本身、扩展宿主、十几万个文件、AI 索引、zram 和多套历史 Server。在高性能开发机上,它们很容易被忽略;换到 4 核 8G 的业务服务器上,每一项都在挤占业务余量。