本文介绍在 Go 中高效序列化大量结构体为换行分隔的 JSON 字符串的方法,重点解决因频繁字符串拼接(+=)引发的内存分配爆炸问题,推荐使用 bytes.Buffer 实现零拷贝式累积写入。
本文介绍在 go 中高效序列化大量结构体为换行分隔的 json 字符串的方法,重点解决因频繁字符串拼接(`+=`)引发的内存分配爆炸问题,推荐使用 `bytes.buffer` 实现零拷贝式累积写入。
在 Go 中,将切片中的每个结构体序列化为 JSON 并以换行符(\n)连接,是一种常见需求(例如生成 NDJSON / JSON Lines 格式)。但若直接采用字符串累加方式:
buffer := ""
for _, record := range all_data {
body, _ := json.Marshal(record)
buffer += string(body) + "\n" // ❌ 危险!每次 += 都创建新字符串,时间复杂度 O(n²),内存占用激增
}该写法存在严重性能缺陷:Go 中字符串是不可变的,buffer += ... 每次都会分配一块全新且更大的底层字节数组,并复制全部历史内容。当 all_data 包含数千或更多元素时,内存分配次数呈平方级增长,极易触发 GC 压力甚至 OOM。
✅ 正确做法是使用 bytes.Buffer —— 它底层基于可动态扩容的 []byte,提供高效的追加写入能力,所有操作均复用同一底层数组,避免重复拷贝:
import (
"bytes"
"encoding/json"
"fmt"
)
var buf bytes.Buffer
for _, record := range all_data {
body, err := json.Marshal(record)
if err != nil {
// 生产环境务必处理错误,不可忽略
log.Printf("failed to marshal record: %v", err)
continue
}
buf.Write(body) // 写入 []byte,无字符串转换开销
buf.WriteByte('\n') // 更轻量:WriteByte 比 WriteString("\n") 更高效
}
result := buf.String() // 仅在最后一次性转换为 string(如需)
// 或直接写入 io.Writer:io.Copy(someWriter, &buf)? 进阶优化建议:
- 若目标是输出到文件、HTTP 响应或网络流,避免调用 .String(),直接使用 buf.Bytes() 或将 *bytes.Buffer 作为 io.Reader 传入 io.Copy(dst, &buf),彻底规避最终字符串拷贝;
- 对于超大数据集,考虑流式处理:用 json.NewEncoder(w io.Writer) 直接编码每个 record 并写入 w(如 bufio.Writer),内存占用恒定 O(1);
- 始终检查 json.Marshal 错误,结构体字段未导出、含不支持类型(如 func、chan)将导致静默失败或 panic。
总结:字符串拼接不是构建长文本的工具,bytes.Buffer 才是 Go 中高效累积二进制/文本数据的标准方案。一次正确的缓冲区选择,即可将内存峰值从 GB 级降至 MB 级,让 JSON Lines 生成既健壮又可扩展。










