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 语言引入切片类型, 内部由包含数组指针、长度、
   243   244   245   246   247   248   249   250   251   252   253