Page 243 - 《软件学报》2026年第3期
P. 243
1206 软件学报 2026 年第 37 卷第 3 期
表 3 连续域访问次数分布
连续域访问次数 数量 比例 (%)
1 18 529 964 93.32
2 1 122 460 5.65
⩾3 204 614 1.03
总计 19 857 038 100.00
QLStat 通过递归方式遍历所有解引用表达式, 计算连续解引用次数. 统计结果如表 4 所示, 与连续域访问次数
的分布类似, 单次解引用操作占绝大多数, 为 97.21%, 连续解引用次数为 2 的占了 2.79%. 上述结果表明, 在 Go 语
言中使用指针分析结果时, 可以对连续解引用次数大于等于 2 的场景采用保守建模策略. 这种方式在显著降低分
析开销的同时, 依然能够提供较高的精度, 满足实际需求.
表 4 连续解引用次数分布
连续解引用次数 数量 比例 (%)
1 305 679 97.21
2 8 765 2.79
总计 314 444 100.00
结论 1. 在基本访存操作中, 域访问操作和解引用操作占有较高的比例, 分别为 25.44% 和 17.63%, 在编译器
优化和静态分析工具设计中应重点关注. 此外, 单次域访问占比超过 93%, 单次解引用操作占比超过 97%. 因此在
设计域敏感分析和使用指针分析结果时, 仅需要考虑单次域访问和单次解引用即可覆盖绝大部分场景. 这种设计
能显著降低分析开销, 同时实现快速且高精度的分析目标.
3.2 接口类型的分析
接口类型变量在 Go 的实现中通过一个结构体表示, 如代码 4 所示, 包含两个域, 其中一个域是类型方法表指
针 tab, 用于描述指向类型描述符或者方法表, 另外一个域是数据指针 data, 用于指向实际数据对象. 当将非接口类
型的值赋给接口类型变量时, 通常需要在堆上分配一块新的空间, 并让 data 指向该新空间以表示新对象. 只有当
非接口类型是指针时, 接口变量的 data 才直接指向原始对象, 而无需额外的堆分配. 因此, 接口类型赋值可能导致
隐式内存分配, 从而引发潜在的内存性能问题.
代码 4. 接口类型实现与接口类型转换.
1. // underlying implementation of non-empty interface
2. type iface struct {
3. tab *itab // type descriptor or table pointer
4. data unsafe.Pointer // data pointer
5. }
6. var a int
7. var i interface{} = a // implicit heap allocation
由于接口类型转换可能导致隐式内存分配, 并对内存性能与程序分析建模产生影响, QLStat 根据 Go 语言官
方规范 [33] , 在抽象语法树层面总结了 5 类可能引发新分配对象的接口操作模式, 如表 5 所示. 在这 5 类模式中,
assign 模式中 LHS 为接口类型, call 模式中形参类型均为接口类型, conversion 模式为显式接口转换, ret 模式中返
回类型为接口类型, send 模式中发送到信道的数据类型为接口类型. 除 conversion 模式为显式转换外, 其他模式均
存在到接口类型的隐式转换. 统计结果显示, assign 和 call 模式占据所有模式中的绝大部分, 分别为 20.16% 和
77.48%, 表明分析和优化与接口类型相关的代码时, 需要重点关注这两类模式. 具体来说, assign 模式对函数内的

