JSON序列化慢因标准库依赖反射,easyjson编译期生成代码可提速3–10倍;推荐用json.RawMessage或gjson延迟/按需解析;避免interface{}解包,应定义精简结构体。
json.Marshal 和 json.Unmarshal 会慢?Go 标准库的 encoding/json 包在运行时依赖反射(reflect)遍历结构体字段、查找标签、动态类型判断,这带来明显开销。尤其当结构体嵌套深、字段多、或高频调用(如 API 服务每秒数千请求)时,CPU 和 GC 压力会快速上升。
常见现象包括:pprof 显示 reflect.Value.Interface 或 json.(*decodeState).object 占高;GC pause 时间随 JSON 流量增长而波动;相同数据量下比 Protobuf 或 msgpack 序列化慢 2–5 倍。
这不是 bug,而是设计取舍:标准库优先保证通用性与开发效率,而非极致性能。
easyjson 替代反射式序列化easyjson 在编译期为结构体生成专用的 MarshalJSON / UnmarshalJSON 方法,完全绕过 reflect,实测吞吐提升 3–10 倍,内存分配减少 80%+。
go install github.com/mailru/easyjson/...@latest
easyjson -all user.go(会生成 user_easyjson.go)json tag,且导出字段首字母大写;不支持匿名字段嵌套自动展开(需显式命名)unsafe 加速字符串转换,若需禁用(如 FIPS 合规场景),加参数 -no-unsafe
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Tags []string `json:"tags,omitempty"`
}
// 生成后,可直接调用:
b, _ := user.MarshalJSON() // 非 json.Marshal(user)
user.UnmarshalJSON(b) // 非 json.Unmarshal(b, &user)
很多服务接收固定格式的第三方 JSON(如 Stripe、Slack webhook),但每次请求都走完整 json.Unmarshal + 字段提取,浪费大量 CPU。
更高效的做法是:用 json.RawMessage 延迟解析关键子字段,或用 gjson 直接按路径提取值。
json.RawMessage 适合“主结构体稳定、子内容动态”的场景(如 event.type 决定后续解析逻辑)gjson.GetBytes(data, "data.items.#.price") 比完整反序列化快 5–20 倍,且零内存分配(返回字符串视图)gjson 不校验 JSON 合法性,输入非法时返回空;若需强校验,仍应先用 json.Valid 快速预检var payload struct {
Event string `json:"event"`
Data json.RawMessage `json:"data"`
}
json.Unmarshal(body, &payload)
if payload.Event == "order.created" {
price := gjson.GetBytes(payload.Data, "total").Number()
}
interface{} 和 map[string]interface{} 的陷阱用 json.Unmarshal 解到 interface{} 或 map[string]interface{} 看似灵活,实则代价极高:所有数字转为 float64,字符串重复分配,嵌套 map 深度越深 GC 越频繁。
生产环境应严格避免将它作为通用解包目标。替代方案:
easyjson 或标准库解析jsoniter 并启用 UseNumber() 避免 float64 强制转
json.NewDecoder(r).Decode(&v),防止一次性加载全部内存真正难处理的是混合类型字段(如 "value": 42 或 "value": "hello"),这时应优先在协议层约束类型,而不是在 Go 层做运行时类型推断。