Go字符串拼接触发nil指针panic问题及原因咨询
Go字符串拼接触发nil指针panic问题分析
问题背景
在Go函数执行字符串拼接时触发nil指针解引用panic,仅出现一次,进程重启后恢复。相关代码及错误信息如下:
代码片段
646: func (m *LineModule) getKey(timestamp int64, log *model.Log, 647: loc model.LocationInfo, retMap map[int64]interface{}) { 648: timeNow := time.Now().Local() 649: project := util.GetProjectName(log.Flag, log.CustomInfo) 650: protocol := util.GetProtocol(log, m.IDMap) 651: version := util.GetVersion(log, m.IDMap) 652: key := log.Server + log.Level + log.Host + loc.Region + loc.Isp + loc.Country + 653: strconv.Itoa(log.Tag) + log.ONT + project + "_" + protocol + version // panic触发点
错误信息
panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x55ad1e73b4c1] goroutine 709 [running]: data/module.(*LineModule).getKey(0xc001bf4500, 0x55ad1f7d8aa0, 0xc0328ef680, {{0x55ad1edf555e, 0x2}, {0xc0337ad207, 0x6}, {0xc001558ed6, 0x6}}, 0xc001562000) /data/module/line_module.go:653 +0x3ec data/module.(*LineModule).calc(0xc02f250f90, {0x55ad1f2b9478, 0xc0328ef680}, 0x0) /data/module/line_module.go:643 +0x247 data/module.(*LineModule).run(0xc001bf4500) /data/module/line_module.go:236 +0x126 created by data/module.(*LineModule).Init.func1 /data/module/line_module.go:180 +0x9b
问题分析
1. panic的根源
panic并非因为调用getKey时log初始为nil(否则第649行访问log.Flag就会直接panic),更大概率是并发竞态条件导致:
- 调用
getKey时log指针是非nil的,前几行(649-651)能正常访问log的字段; - 在执行到第653行之前,其他goroutine异步将
log指针修改为nil,此时访问log.Tag或log.ONT就会触发nil指针解引用panic; - 另一种可能是
model.Log的某个字段(比如ONT)是指针类型,被其他goroutine置为nil,访问该字段时触发panic。
2. 为什么第649行未触发panic
因为函数执行初期log指针是非nil的,前几行代码能正常访问log的成员。只有当后续代码执行时,log指针被外部goroutine修改为nil,才会在第653行触发panic。这种场景完全依赖goroutine的调度时机,属于偶发的竞态问题。
3. 为什么问题仅出现一次
竞态条件的触发需要极端巧合的调度时机:多个goroutine的执行节奏刚好凑成“前几行用log时非nil,到第653行时log被置为nil”。进程重启后,系统负载、goroutine调度顺序发生变化,不再满足这个巧合条件,因此问题不再复现。
解决方案
- 前置nil检查:在函数开头添加
if log == nil { return }(或根据业务逻辑做兜底处理),避免后续访问nil指针; - 保证并发安全:如果
log对象会被多个goroutine共享修改,需使用互斥锁(sync.Mutex)保护访问,或者改为传递model.Log的值而非指针; - 检查字段类型:确认
model.Log的所有字段是否为值类型,若存在指针类型字段,同样要保证其访问和修改的并发安全性。
内容的提问来源于stack exchange,提问作者Michael96
相关产品推荐
相关产品推荐

