Go语言中json.NewDecoder与json.Unmarshal内存占用差异疑问
json.Decoder.Decode与io.ReadAll+json.Unmarshal的内存差异原因 这两种JSON解析方式的内存占用差异,核心原因在于三点:
内部缓冲机制的区别
json.NewDecoder内部自带一个动态扩容的缓冲,初始大小为4KB。在流式读取响应体并解析的过程中,它会根据数据量不断扩容缓冲空间。如果你的API返回的JSON数据量大、结构复杂,这个缓冲可能会多次扩容,而且解析完成后,缓冲的内存不会立即释放(会保留在Decoder实例里)。要是你的代码是批量或高频调用解析逻辑,这些残留的缓冲内存会不断累积,直接推高整体内存占用。
而io.ReadAll会一次性把响应体的所有数据读取到一个byte切片中,最终返回的切片容量基本匹配数据长度(仅有极小冗余),json.Unmarshal直接基于这个完整切片解析,不需要额外的中间缓冲,内存分配更紧凑。内存分配与回收的效率差异
Decode是边读边解析,过程中会产生大量细碎的临时内存分配——比如解析嵌套结构时的临时变量、暂存的部分JSON片段。这些小内存块的GC回收效率远不如集中的大内存块。另外,Decoder实例本身的字段(比如缓冲、解析状态)也会占用额外内存,要是没及时被GC回收,会进一步拉高内存占用。ReadAll+Unmarshal的方式则不同:只有一次集中的byte切片分配,解析完成后如果这个切片没有被后续代码引用,会很快被GC回收,整体内存占用更可控。解析逻辑的内存开销差异
流式解析时,Decoder需要随时处理部分读取的JSON数据,为了处理回溯、语法校验等逻辑,会额外分配内存暂存中间状态;而Unmarshal基于完整的byte切片,能直接定位所有数据的位置,不需要额外的状态暂存内存,解析过程的内存开销更低。
结合你从多个API汇总数据的场景,如果是批量请求加解析,Decode方式的缓冲累积和细碎内存分配会把内存占用放大,而ReadAll+Unmarshal的内存使用更高效,所以会出现150MB左右的明显差异。
内容的提问来源于stack exchange,提问作者yldshv

