Go首次循环正常,二次迭代触发nil指针解引用panic求助
问题根源与解决方案
这个问题的核心和GC完全没关系,问题出在HTTP响应体res.Body的特性上——它是一个只能被读取一次的io.ReadCloser流!
你提到edgar.GetCompanyFilingListRes会消费res.Body,这意味着第一次循环时,这个函数已经把Body里的数据流读完了,甚至很可能在读取完成后调用了Body.Close()(这是处理HTTP响应的常规操作)。等到第二次循环再把同一个res传进去时,Body已经处于被关闭/读取完毕的状态,此时尝试访问它自然会触发nil pointer dereference的panic。
解决方案
根据你的场景,有两种常见的解决思路:
方案1:复用响应体内容(若能修改GetCompanyFilingListRes)
如果可以修改edgar包的GetCompanyFilingListRes函数,建议先把res.Body的内容完整读取到内存中,再基于内存数据处理,这样就能多次复用数据:
// 修改后的GetCompanyFilingListRes示例 func GetCompanyFilingListRes(res *http.Response) []map[string]interface{} { // 先把Body内容读入内存 bodyBytes, err := io.ReadAll(res.Body) if err != nil { // 按需处理错误 return nil } // 务必关闭Body释放资源 res.Body.Close() // 后续基于bodyBytes解析数据(比如JSON反序列化等) // ... // 返回处理后的map切片 }
修改后你就可以在循环外只发起一次请求,多次复用解析后的内存数据。
方案2:每次循环重新发起请求(无法修改函数时)
如果没办法修改GetCompanyFilingListRes,最简单的办法就是把HTTP请求放到循环内部,每次迭代都获取一个全新的响应对象,这样每次都有未被消费的Body可以使用:
package main import ( "data/edgar" "fmt" "net/http" ) func main() { url := "calltomylocalserver.com/page.html" for i := 0; i < 100000; i++ { // 每次循环重新发起请求 res, err := http.Get(url) if err != nil { fmt.Printf("第%d次请求失败: %v\n", i, err) continue } m := edgar.GetCompanyFilingListRes(res) fmt.Println(m) // 手动关闭Body,避免资源泄漏 _ = res.Body.Close() } }
这里要提醒你:之前的代码忽略了http.Get的错误返回,这是很危险的编程习惯,一定要加上错误处理逻辑。
再强调一次:这个问题和GC无关——你的res变量在循环中一直被引用,GC不会回收它。问题的本质就是HTTP响应体的一次性消费特性,只要解决了Body的复用/重新获取问题,panic就会消失。
内容的提问来源于stack exchange,提问作者AppTest
相关产品推荐
相关产品推荐

