Page 258 - 《软件学报》2026年第3期
P. 258

李清伟 等: Go  语言程序的内存性能与安全问题实证研究                                                   1221


                 语言程序的数据竞争、死锁问题. 另外, 由于             Go  语言是类型安全的语言, 而       unsafe package 会破坏类型安全机制,
                 已有实证研究探索了        unsafe package 在真实世界中使用情况以及潜在危害          [41,84] . 但是对除并发安全、类型安全之
                 外, 针对  Go  语言中其他内存问题的研究工作相对较少. 例如, 对于内存泄漏问题来说, 在                    Go  语言相关的研究工作
                 中  Saioc 等人  [85]  通过检测部分死锁的  Go  协程  Goroutine 来检测内存泄漏问题. CodeQL   分析的已有工作     [86,87] 主要
                 聚焦于漏洞检测. 本工作的创新之处在于, 利用             CodeQL  这种将程序代码解析存储到数据库中, 然后使用查询语言
                 进行查询得到代码查询结果的范式, 实现对现实世界                 Go  项目中内存相关基本操作分布情况的统计分析. 同时, 基
                 于这一方法, 本工作对大量真实世界           Go  项目进行了   L2-3  模式的检查, 给出可能存在内存泄漏问题的报告.
                    ● 基于静态代码模式的优化. 特定的静态代码模式可能造成性能问题. 对这些模式                         (包括静态分析所建模的语
                 句) 的统计分析有助于指导静态分析和程序优化的方向, 从而缓解性能问题. 例如, He 等人                       [88] 基于  3  类普遍存在的
                 容器使用模式, 识别可能影响分析精度的依赖上下文的对象, 在分析时只为这些对象添加上下文, 而不为其他对象
                 添加, 从而在接近相同的精度下显著提升指针分析速度. 本文对连续域访问次数和连续解引用的次数也表明: 在分
                 析这两类模式时, 仅对单次操作进行比较准确的建模即可获得较大的收益, 对于多次操作可以采取保守的建模方式
                 以获得较快的分析速度. Selakovic 等人      [10] 的工作对  JavaScript 中导致性能问题的常见静态代码模式进行了总结, 指
                 导应用开发者避免常见的错误, 指导研究者开发相应的性能工具, 指导                     JavaScript 引擎开发者处理这些流行的性能
                 瓶颈模式. 本文在第     3  节针对  Go  语言中的基本访存操作进行了建模, 发现域访问操作和解引用操作在所有基本访
                 存操作中合计占约      42%, 是潜在的性能优化点, 需要在程序建模时重点关注. 进一步分析表明, 在所有转换为接口的
                 模式中, 赋值语句和调用语句中包含接口转换的模式最多, 约占                   98%. 同时, 在这些转换接口的模式中, 被转换类型
                 为非指针类型的模式占        34.96%. 这些模式可能存在隐式堆内存分配, 内存分配优化可以重点关注这些模式.
                  8   总 结

                    本文提出了一套针对        GitHub  中开源  Go  仓库的实证分析框架, 并利用该框架对          GitHub  中筛选出来的   996  个
                 Go  语言仓库中的内存访问模式、内存安全问题以及修复模式进行了实证分析与总结.
                    首先, 针对   Go  语言的内存访问模式, 本文使用        QLStat 对仓库进行了自动统计与分析, 分析结果显示解引用和
                 域访问操作分别占       17.63%  和  25.44%  的比例. 这一结果说明, 在进行编译器优化和静态分析工具建模时, 必须重
                 点关注这两类操作. 进一步分析发现, 大多数的域访问和解引用操作均为单次访问. 与此同时, 针对                               Go  独有的
                 interface 类型, 分析结果表明, 在转换为接口的模式中, assign       模式和   call 模式占据了绝大多数, 在分析接口相关的
                 内存分配时需要着重考虑.
                    其次, 本文通过人工分析        130  个  Issues/PRs, 并留下  44  条确定为内存问题的  Issues/PRs. 这些  Issues 的类别分
                 为内存泄漏、无效地址或空指针访问、悬垂指针这                   3  类. 进一步的模式分析显示, 内存安全问题在开源             Go  语言
                 项目中频繁出现, 并且最常见的类型是无效地址或空指针访问问题, 而开发工具、云/微服务、容器/虚拟机等领域
                 的项目更容易发生内存安全问题. 此外, 针对每一类的内存安全问题, 本文也进行了模式的细分.
                    最后, 针对模式细分中的        L2-3  问题模式  (即由切片表达式赋值       s3 = s2[start:] 导致内存泄漏的问题模式), 本文
                 使用  QLStat 框架开发了代码检查工具, 分析了         966  个项目数据库, 报告了     574  处问题. 在这些问题中, 人工审查了
                 70  个问题, 最终发现   6  个可能正确的报告, 并通过       GitHub Issues 提交给相关仓库, 最终有     1  个被开发者确认为可
                 能存在内存泄漏.

                 References
                  [1]   Go. Case studies. 2025. https://go.dev/solutions/case-studies
                  [2]   Go. Proposal: arena: New package providing memory arenas. 2025. https://github.com/golang/go/issues/51317
                  [3]   Wang C, Zhang MR, Jiang Y, Zhang HF, Xing ZC, Gu M. Escape from escape analysis of Golang. In: Proc. of the 42nd ACM/IEEE Int’l
                     Conf. on Software Engineering: Software Engineering in Practice. Seoul: ACM, 2020. 142–151. [doi: 10.1145/3377813.3381368]
                  [4]   Ding  BY,  Li  QW,  Zhang  Y,  Tang  FG,  Chen  JB.  MEA2:  A  lightweight  field-sensitive  escape  analysis  with  points-to  calculation  for
                     Golang. Proc. of the ACM on Programming Languages, 2024, 8: 1362–1389. [doi: 10.1145/3689759]
   253   254   255   256   257   258   259   260   261   262   263