Page 248 - 《软件学报》2026年第3期
P. 248
李清伟 等: Go 语言程序的内存性能与安全问题实证研究 1211
版问题. 具体来说, Resource 结构体的 Close 函数没有同时关闭配对的 pairResource 资源, 导致配对资源无法被回
收, 从而产生内存泄漏.
L1-2: 资源使用后因缺失对其释放函数的调用导致资源无法被正确回收. 代码 5 中第 7–9 行展示了 grpc-go
的 Issue#6642 [44] 的代码简化. 在创建并使用 ticker 进行计时之后, 缺少对其释放函数 Stop 的延迟调用, 导致 ticker
资源无法被回收. 仓库的贡献者评论指出, 此类问题并非首次出现, 而会长期不断发生 [45] . 除了因忘记延迟释放资
源而导致的内存泄漏问题外, 还存在在错误处理时缺失资源释放操作的情况, 即代码 5 中第 10–14 行, 该部分展示
了 tidb 的 Issue#34666 [46] 的简化版. 在错误/异常处理分支, 遗漏对已分配资源的释放, 导致在异常发生时产生内存
泄漏. 另一种情况如代码 5 第 15–20 行的 L1-2-otherref 模式, 简化自 btcd 的 Issue#2105 [47] , 此时资源对象在不同条
件下被不同对象引用, 某个特定条件下遗漏了通过相应引用对象释放对资源的引用. 在该问题中, 布尔型变量
c.batch 指示资源 element 是被 c.batchList 还是被 c.requestList 引用的. 修复前的代码未考虑当 c.batch 为 true 时,
需要从 c.batchList 中删除对资源 element 的引用, 潜在引起资源泄漏.
● L2: 不正确的显式释放导致无效资源仍然被隐式引用. 在该问题类中包含 3 个小类, 模式代码如代码 6
所示.
代码 6. L2: 不正确的显式释放问题模式.
1. // L2-1: grpc/grpc−go/issues/2444
2. + resource.cancel()
3. if someCondition {
4. return // return too early
5. }
6. − resource.cancel() // resource not cancelled before return
7. // L2-2: kubernetes-sigs/cluster-api/issues/9542
8. − resource = createResource()
9. if someCondition {
10. − resource = createResource() // create twice
11. }
12. + resource = createResource()
13. resource.stop() // stop once
14. // L2-3: grpc/grpc−go/issues/4758
15. − item, q.queue = q.queue[0], q.queue[1:] // q.queue[0] will not be used forever
16. + item = q.queue[0]
17. + q.queue[0] = nil
18. + q.queue = q.queue[1:]
19. ... // destroy item after return
L2-1: 因逻辑错误导致资源的显式释放函数未被调用, 引起内存泄漏. 代码 6 中第 1–5 行展示了 grpc-go 的
Issue#2444 [48] 的代码简化, 由于 if 条件的提前返回, 导致资源的释放函数 cancel 未被调用, 致使资源未被释放.
L2-2: 自行内存管理下分配次数和释放次数不匹配, 导致内存泄漏. 代码 6 中第 7–13 行展示了 cluster-api 的
Issue#9542 [49] 的代码简化, 在某些执行路径下, 资源会被创建两次, 而最终资源释放时却只释放一次, 这导致其中一
份资源无法被正确回收, 引起内存泄漏.
L2-3: 切片表达式 s[start:end] [50] 可能会使隐式引用未被删除, 导致内存泄漏. Go 使用标记-清扫方法回收不可
达对象. 当一个对象不再被任何指针所引用时, 它可以被回收. Go 语言引入切片类型, 内部由包含数组指针、长度、

