共130行
STW: Go 、Java、Erlang
2026-05-05 01:59:03
Golang STW vs Java STW
Golang STW 整体远短于 Java,且频率更低、影响更小:Go 仅两次微秒级暂停;Java 多数 GC 存在毫秒–秒级暂停,ZGC/Shenandoah 可做到亚毫秒但仍有开销。
一、基本概念
- STW(Stop-The-World):GC 为获取一致性内存快照,暂停所有用户线程/协程,仅 GC 线程运行。
- Golang:GC 为并发三色标记-清扫(非分代、非移动),仅两次短暂 STW。
- Java:JVM GC 分代(年轻代/老年代),不同收集器 STW 差异大。
二、Golang STW 机制(Go 1.5+)
1. 触发阶段(仅两次)
- Mark Setup(标记启动):开启写屏障、准备根扫描;STW 微秒级(≈10–100μs)。
- Mark Termination(标记终止):完成最终标记、关闭写屏障;STW 微秒级(≈10–100μs)。
- 其余阶段(并发标记、并发清扫)与用户 goroutine 并行,无 STW。
2. 关键优化
- 写屏障(Write Barrier):指针写操作触发,跟踪并发修改,避免漏标。
- 安全点(Safe Point):goroutine 仅在函数调用、循环结束等位置暂停,延迟极低。
- 与堆大小无关:STW 时长主要取决于
GOMAXPROCS(P 数量),而非堆内存大小。
三、Java STW 机制(主流收集器)
1. 经典收集器(STW 严重)
- Serial/Serial Old:年轻代/老年代 GC 全程 STW,单线程,毫秒–秒级。
- Parallel/Parallel Old:全程 STW,多线程,暂停略短(≈50–200ms)。
2. 并发收集器(部分阶段 STW)
- CMS(Concurrent Mark Sweep):
- 初始标记(STW,≈10–50ms)
- 并发标记(无 STW)
- 重新标记(STW,≈20–100ms)
- 并发清除(无 STW)
- G1(Garbage-First):
- 年轻代 GC:全程 STW(≈10–50ms)
- 混合 GC:初始标记(STW,≈5–20ms)、最终标记(STW,≈5–20ms)
3. 低延迟收集器(亚毫秒级 STW)
- ZGC(Z Garbage Collector,Java 11+):几乎无 STW,仅短暂根扫描(<1ms),并发度极高。
- Shenandoah(Java 12+):几乎无 STW,支持并发压缩,暂停 <1ms。
四、核心对比表
| 对比维度 |
Golang(Go 1.5+) |
Java(主流收集器) |
| STW 次数 |
2 次/GC |
1–多次/GC(取决于收集器) |
| 单次时长 |
微秒级(10–100μs) |
经典:毫秒–秒级;ZGC/Shenandoah:<1ms |
| 触发阶段 |
Mark Setup、Mark Termination |
初始标记、重新标记、Full GC 全程等 |
| 与堆大小关系 |
无关 |
强相关(堆越大,STW 越长) |
| 分代 |
无分代 |
年轻代+老年代,Full GC 更耗时 |
| 写屏障 |
有(Dijkstra 插入式) |
有(不同收集器实现不同) |
| 适用场景 |
高并发、低延迟(微服务、网关) |
企业级应用(需调优 GC) |
五、影响与选型建议
- Golang 优势:STW 极短,高并发下响应稳定,适合对延迟敏感的服务(如 API 网关、消息队列)。
- Java 优势:成熟生态、分代 GC 吞吐高,ZGC/Shenandoah 可满足低延迟需求,适合复杂企业应用。
- 选型参考:
- 低延迟+高并发:选 Golang。
- 复杂业务+生态依赖:选 Java(ZGC/Shenandoah)。
六、总结
Golang 通过并发三色标记+两次微秒级 STW,将暂停影响降至极低;Java 传统收集器 STW 较重,现代 ZGC/Shenandoah 已接近 Go 水平,但仍有额外开销。理解差异有助于根据业务场景选择合适语言与 GC 策略。
要不要我整理一份 Go 与 Java 的 GC 调优清单(含关键参数、常用工具和典型问题排查步骤)?
Erlang STW
Erlang 无全局STW,只有单进程级STW;粒度最细、全局零暂停、延迟最稳,远优于Go与Java。
一、核心设计:无共享堆,进程独立GC
Erlang VM(BEAM)的核心是每个进程独占私有堆(heap),进程间无共享内存,仅靠消息传递通信。
- Go/Java:全局共享堆,GC必须全局协调,必然有全局STW。
- Erlang:GC以单个进程为单位,互不干扰;回收A进程时,B/C/D进程继续调度运行,无全局暂停。
二、Erlang的“STW”:仅进程级,毫秒内
1. 单进程GC过程(分代拷贝)
每个进程私有堆默认233字(≈2KB),按斐波那契数列扩容;GC为分代拷贝(Generational Copying):
- Minor GC(年轻代):仅扫描/复制年轻代,STW≈微秒–亚毫秒。
- Major GC(全堆):扫描/复制整个进程堆,STW≈1–5ms(堆越大越长,但进程堆通常很小)。
2. 关键:进程级暂停,非全局
- 暂停范围:仅当前被GC的进程,其他进程正常运行、调度、收发消息。
- 调度保障:GC进程达到**reduction配额(默认2000)**会被强制让出CPU,避免长时间阻塞调度器。
- 大进程优化:超大堆进程GC会切到dirty scheduler(后台线程),不阻塞普通调度器,进一步降低影响。
三、Go/Java/Erlang STW 对比表
| 对比维度 |
Erlang(BEAM) |
Golang(Go 1.5+) |
Java(ZGC/Shenandoah) |
| STW范围 |
单进程级(无全局STW) |
全局(两次微秒级) |
全局(亚毫秒级) |
| STW次数 |
每进程GC 1次 |
每GC 2次 |
每GC 1–多次 |
| 单次时长 |
微秒–5ms(进程堆) |
10–100μs |
<1ms |
| 与堆大小关系 |
仅与单进程堆相关 |
无关 |
弱相关(优化后) |
| 全局抖动 |
无 |
极低 |
极低 |
| 并发模型 |
Actor模型,无共享 |
CSP,共享内存 |
线程,共享内存 |
四、为什么Erlang几乎无抖动?
- 极致隔离:进程私有堆+无共享状态,GC天然隔离,互不影响。
- 轻量进程:百万级进程,每个堆极小(KB级),单进程GC极快。
- 软实时调度:BEAM调度器按时间片公平调度,GC不阻塞调度器,延迟稳定。
- 不可变数据:变量不可变,无写屏障开销,GC逻辑更简单高效。
五、适用场景与局限
优势场景
- 软实时系统:如电信交换机、5G核心网、实时音视频(WhatsApp早期用Erlang支撑9亿用户)。
- 高并发高可用:消息队列、物联网网关、分布式数据库(如Riak)。
- 低延迟稳定性:对抖动敏感的金融交易、实时监控系统。
局限
- 单进程CPU密集:大内存/大计算进程的GC会变慢(但仍不影响全局)。
- 动态类型开销:性能略低于静态编译的Go/Java,但延迟更稳。
六、总结
- Erlang:零全局STW,仅单进程微秒–毫秒级暂停,粒度最细、延迟最稳。
- Golang:两次全局微秒级STW,抖动极低但仍有全局暂停。
- Java:传统收集器毫秒–秒级STW;ZGC/Shenandoah亚毫秒级,仍有全局协调开销。