环境:macOS arm64 (Apple Silicon),32GB RAM,Ghostty 终端
这台机器用了好几年了。各种开发环境装了又卸、卸了又装,.zshrc 和 .bash_profile 里塞满了历史遗留的 PATH、重复的插件、已经不存在的目录。终端启动越来越慢,每次开个新 tab 都要愣一下。
一直知道该清理,但总觉得"下次再说"。直到最近用 Claude + GLM 5.1 试了一下,从诊断到改完,前后大概十来分钟。
结果是从 1.3 秒降到了 0.2 秒。
先看看有多慢
time zsh -i -c exit
跑一下,1.303 秒。开个终端等一秒多,体感确实很差。
怎么查的
整体计时知道了慢,但不知道慢在哪。有两种方式定位:
逐个 source 测耗时:
time zsh -c "source /opt/homebrew/opt/nvm/nvm.sh"
time zsh -c "source $HOME/.sdkman/bin/sdkman-init.sh"
time zsh -c "eval \$(/opt/homebrew/bin/brew shellenv)"
zprof 函数级分析:
在 .zshrc 开头加 zmodload zsh/zprof,末尾加 zprof,能得到精确到每个函数的耗时排名。这个最管用。
到底是什么在拖后腿
zprof 一跑,问题很清楚:
- nvm 吃了 ~540ms,占了一半多。它每次启动都扫描所有 Node 版本,完全没必要。
- compinit(补全系统)~360ms,6 个插件的补全初始化开销。
- SDKMAN ~300ms,Java 版本管理器初始化。
- brew / bun / cargo 这些加起来 ~120ms,影响不大。
但除了这些"大头",还有一堆乱七八糟的问题:
zsh-syntax-highlighting和fast-syntax-highlighting功能完全一样,同时加载着- ServBay 环境块在
.zshrc和.bash_profile各出现一次 - SDKMAN 也加载了两次,同上
- Antigravity PATH 在
.zshrc里写了两次 - datax、codebuddy、yarn global 等目录早就不存在了,PATH 还留着
.zshenv里重复加载cargo/envarch -arm64 brew这个 alias 在原生 arm64 机器上完全多余
说白了,就是年久失修。每次装个新工具就往配置文件里塞一行,从来没清理过。
改了什么
nvm 懒加载(省 ~500ms,效果最大)
原来每次开终端都加载 nvm,但大部分时候根本不用 node。改成懒加载——首次调用 node/npm/npx 时才初始化:
export NVM_DIR="$HOME/.nvm"
lazy_load_nvm() {
unset -f nvm node npm npx pnpm
[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && \. "/opt/homebrew/opt/nvm/nvm.sh"
[ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && \. "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm"
}
nvm() { lazy_load_nvm; nvm "$@"; }
node() { lazy_load_nvm; node "$@"; }
npm() { lazy_load_nvm; npm "$@"; }
npx() { lazy_load_nvm; npx "$@"; }
pnpm() { lazy_load_nvm; pnpm "$@"; }
SDKMAN 懒加载(省 ~300ms)
同理,java/sdk/mvn 这些也不需要每次都加载:
export SDKMAN_DIR="$HOME/.sdkman"
lazy_load_sdkman() {
unset -f sdk java javac jar mvn gradle
[[ -s "$HOME/.sdkman/bin/sdkman-init.sh" ]] && source "$HOME/.sdkman/bin/sdkman-init.sh"
}
sdk() { lazy_load_sdkman; sdk "$@"; }
java() { lazy_load_sdkman; java "$@"; }
javac() { lazy_load_sdkman; javac "$@"; }
jar() { lazy_load_sdkman; jar "$@"; }
mvn() { lazy_load_sdkman; mvn "$@"; }
gradle() { lazy_load_sdkman; gradle "$@"; }
精简插件
去掉了 zsh-autocomplete(bug 多、资源占用高)和 fast-syntax-highlighting(跟另一个重复):
# 之前
plugins=(git poetry zsh-autosuggestions zsh-syntax-highlighting fast-syntax-highlighting zsh-autocomplete)
# 之后
plugins=(git poetry zsh-autosuggestions zsh-syntax-highlighting)
清理重复和失效的配置
删掉的东西不少,列一下:
重复的:
- ServBay 环境块(
.zshrc里有,.bash_profile里也有) - SDKMAN 加载(同上)
- Antigravity PATH(
.zshrc自己内部就写了两次) - cargo/env(
.zshenv和.bash_profile都加载了)
失效的:
$PYENV_ROOT/bin— 目录不存在DATAX_HOME— 目录不存在$PATH.末尾多了个.— 语法错误yarn/global/node_modules/.bin— 目录不存在.codebuddy/bin— 目录不存在arch -arm64 brewalias — arm64 原生机器不需要这个包装
最终结果
| 阶段 | 启动时间 |
|---|---|
| 优化前 | 1.303s |
| 懒加载 + 插件精简后 | 0.549s |
| 清理失效 PATH 后 | 0.208s |
从 1.3 秒到 0.2 秒,快了大概 6 倍。
有两个小事项:每个终端会话里首次跑 node 或 java 会有一次 0.5-0.8 秒的加载延迟,之后就不会了。另外 zsh -i -c exit 测试时可能会报 gitstatus 初始化失败,这是因为非交互模式缺少完整 TTY,正常使用不会碰到。
整个过程,从打开终端发现问题到改完验证,十几分钟。之前拖了大半年没动的东西,让 AI 帮忙梳理一下就清掉了。这种"知道自己该做但一直没做"的事情,有个帮手确实不一样。

