【问题标题】:Returning pointer on struct member or embedded struct in Go?在 Go 中返回结构成员或嵌入结构的指针?
【发布时间】:2017-08-12 06:17:53
【问题描述】:

在 Go 中获取地址并返回指向函数内部局部变量的指针会导致编译器将其分配在堆上而不是堆栈上,因此返回的指针仍然有效。

现在如果我获取并返回结构成员或嵌入式结构的地址会发生什么?

type A struct {
    a,b,c int
}
type B struct {
    A
    d,e,f int
}
func (b *B) get1() *A {
    return &b.A
}
func (b *B) get2() *A {
    localB := B{}
    return &localB.A
}

编译器会在堆上分配嵌入式结构 A 并将 B 的成员保留在堆栈上吗?

即使 localB.A 引用仍在使用中,垃圾收集器也会收集 localB 吗?

如果通过反射访问,编译器如何确定何时将嵌入式结构保留在堆栈或堆上?

【问题讨论】:

  • Go 在这里很安全。你只是不能产生悬空指针,你做什么都没关系(如果你远离包不安全)。编译器在这里玩得很安全:如果它不能证明堆栈上的分配是安全的,它将在堆上分配。没什么可担心的;继续前进。
  • @Volker 我不担心悬空指针,但我担心封闭结构不会很快被垃圾收集。
  • 首先:类型 A 是空的,因此在您的示例中没有要收集的内容。假设 A 在现实生活中不是空的,那么如果 localB.A 正在使用,GC 如何收集 localB?您是否假设 GC 已损坏?
  • 假设A不为空并且B除了A之外还有其他成员,那么是的,就像答案之一的逃逸分析链接中提到的那样,Go逃逸分析并不完美,因此垃圾收集不是也很完美。所以我们可以在一定程度上说它是“破碎的”或更好的词——不完美。现在,如果我的结构 B 很大而 A 很小,那么这个缺陷对于了解我是否处理数百万个 B 很重要。我可以想象,即使 A 无法收集,理想的 GC 也应该有可能对 B 的部分进行垃圾收集。

标签: go pointers struct heap-memory


【解决方案1】:

编译器执行escape analysis 来确定变量是否可以在创建它的范围之外使用,在这种情况下,它必须在堆上分配。如果编译器可以确保不会发生这种情况,它会在堆栈上分配变量。

但是,这是编译器当前的行为,但规范中没有提及,因此可能会在未来的任何版本中发生变化。

【讨论】:

    【解决方案2】:

    为什么你甚至想知道这些细节?

    引用Go FAQ(强调我的):

    从正确性的角度来看,您不需要知道。每个变量 只要有对它的引用,在 Go 中就存在。存储 实现选择的位置与语义无关 语言。

    在当前的编译器中,如果一个变量的地址被占用,那 变量是在堆上分配的候选。然而,一个基本的 逃逸分析可以识别某些情况,这些变量不会 超过函数的返回值,并且可以驻留在堆栈中。

    编译器和垃圾收集器的行为严格依赖于实现,并且会随着不同版本的变化而变化。 你无法证明你的开场白总是正确的:编译器可能会根据你的函数的编写或调用方式决定应用进一步的优化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-20
      • 2023-03-12
      • 1970-01-01
      相关资源
      最近更新 更多