Go语言处理700MB单行XML文件内存不足,如何优化?
解决大XML文件格式化时的内存溢出问题
你的代码处理大文件时出现OOM,核心问题是一次性把整个700MB文件加载到内存中:io.ReadAll会把所有内容读进内存,转成字符串后调用xmlfmt.FormatXML,格式化后的结果又会占用额外内存,总内存占用至少是原文件的2-3倍,直接超出内存限制。而且xmlfmt库本身是基于完整字符串处理的,不支持流式操作,天生不适合大文件场景。
要解决这个问题,必须改用流式解析+流式输出的方式,用Go标准库的xml.Decoder逐节点处理XML,边解析边输出格式化后的内容,这样内存占用只和当前处理的单个节点大小有关,完全不会出现OOM。
改进后的代码
package main import ( "bufio" "encoding/xml" "os" "strings" ) func main() { // 打开输入文件 inputFile, err := os.Open("file.xml") if err != nil { panic(err) } defer inputFile.Close() // 创建输出文件 outputFile, err := os.Create("newFile.xml") if err != nil { panic(err) } defer outputFile.Close() writer := bufio.NewWriter(outputFile) defer writer.Flush() // 初始化XML解码器 decoder := xml.NewDecoder(inputFile) // 若XML存在格式不规范可关闭严格模式,默认Strict=true即可 // decoder.Strict = false indentLevel := 0 indentStr := " " // 保持原代码的缩进设置 for { token, err := decoder.Token() if err != nil { if err.Error() == "EOF" { break } panic(err) } switch t := token.(type) { case xml.StartElement: // 输出当前层级缩进 writer.WriteString(strings.Repeat(indentStr, indentLevel)) // 写入开始标签 writer.WriteString("<" + t.Name.Local) // 写入标签属性 for _, attr := range t.Attr { writer.WriteString(" " + attr.Name.Local + "=\"" + attr.Value + "\"") } writer.WriteString(">\n") indentLevel++ case xml.EndElement: indentLevel-- // 输出当前层级缩进 writer.WriteString(strings.Repeat(indentStr, indentLevel)) // 写入结束标签 writer.WriteString("</" + t.Name.Local + ">\n") case xml.CharData: // 清理原单行XML中的冗余空白,只保留有效内容 trimmed := strings.TrimSpace(string(t)) if trimmed != "" { writer.WriteString(strings.Repeat(indentStr, indentLevel)) writer.WriteString(trimmed + "\n") } case xml.Comment: // 保留注释并格式化缩进 writer.WriteString(strings.Repeat(indentStr, indentLevel)) writer.WriteString("<!--" + string(t) + "-->\n") case xml.ProcInst: // 处理XML声明等指令 writer.WriteString("<?xml " + string(t.Inst) + "?>\n") } } }
代码说明
- 流式解析:
xml.Decoder逐字节读取输入文件,每次仅解析一个XML token(开始标签、结束标签、字符数据等),不会一次性加载整个文件。 - 流式输出:用
bufio.Writer批量写入输出,既保证写入效率,又避免在内存中缓存大量数据。 - 缩进控制:通过
indentLevel变量跟踪当前层级,遇到开始标签增加缩进,结束标签减少缩进,保证格式化后的XML结构清晰。 - 空白处理:对字符数据做trim操作,去除原单行XML中多余的空白字符,避免格式化后出现大量空行。
这种方式不管XML文件是700MB还是几个GB,内存占用都会保持在很低的水平,彻底解决内存溢出问题。
内容的提问来源于stack exchange,提问作者André Ribeiro
相关产品推荐
相关产品推荐

