英文:
How are Go type method sets allocated in memory?
问题
C++避免在每次创建实例时为类方法分配内存。我直觉上认为Go也会减少这种重复。只是为了确认一下,Go是否只存储自定义结构体的方法集一次?
type Custom struct {
value string
}
func (c Custom) TurnItUp() {
c.value = "up"
}
func (c Custom) TurnItDown() {
c.value = "down"
}
... // 为Custom定义了更多的方法。
//(在100个维度中的正向和负向)
func main() {
var many []Custom
fmt.Println("Memory: ", foo.memory()) // 测量使用的内存。
for i := 0; i < 10000; i++ {
many = append(many, Custom{value: "nowhere"})
}
fmt.Println("Memory: ", foo.memory()) // 测量使用的内存。
}
英文:
C++ avoids allocating memory for class methods every time an instance is created. My gut feeling is to assume that Go also mitigates this kind of duplication. Just to confirm, does Go store the method set of a custom struct only once?
type Custom struct {
value string
}
func (c Custom) TurnItUp() {
c.value = "up"
}
func (c Custom) TurnItDown() {
c.value = "down"
}
... // Many more methods defined for Custom.
// (Positive and negative directions in 100 dimensions)
func main() {
var many []Custom
fmt.Println("Memory: ", foo.memory()) // Measure memory used.
for i := 0; i < 10000; i++ {
append(many, Custom{value: "nowhere"})
}
fmt.Println("Memory: ", foo.memory()) // Measure memory used.
}
答案1
得分: 3
运行时在将具体类型分配给接口类型时会分配一个 itable。具体类型和接口类型的 itable 被缓存并在后续的分配中使用。
举个例子,下面的代码将分配一个 itable:
type Volume interface {
TurnItUp()
TurnItDown()
}
var many []Volume
for i := 0; i < 10000; i++ {
many = append(many, Custom{value: "nowhere"})
}
而下面的代码将分配两个 itable,一个是 (Custom, Upper) 的,另一个是 (Custom, Downer) 的:
type Upper interface {
TurnItUp()
}
type Downer interface {
TurnItDown()
}
var uppers []Upper
var downers []Downer
for i := 0; i < 10000; i++ {
uppers = append(uppers, Custom{value: "nowhere"})
downers = append(downers, Custom{value: "nowhere"})
}
因为问题中的示例没有将 Custom 值分配给接口,所以不会创建 itable。
运行时使用静态元数据来构建 itable。静态数据只会被分配和初始化一次。
详细信息请参考 Go 数据结构:接口。
英文:
The runtime allocates an itable when a concrete type is assigned to an interface type. The itable for the concrete type and interface type is cached and used on later assignments.
As an example, this code will allocate one itable:
type Volume interface {
TurnItUp()
TurnItDown()
}
var many []Volume
for i := 0; i < 10000; i++ {
many = append(many, Custom{value: "nowhere"})
}
and this code will allocate two itables, one for (Custom, Upper) and one for (Custom, Downer):
type Upper interface {
TurnItUp()
}
type Downer interface {
TurnItDown()
}
var uppers []Upper
var downers []Downer
for i := 0; i < 10000; i++ {
uppers = append(uppers, Custom{value: "nowhere"})
downers = append(downers, Custom{value: "nowhere"})
}
Because the example in the question does not assign a Custom value to an interface, no itables are created.
The runtime uses static metadata data to construct itables. The static data is allocated and initialized once.
See Go Data Structures: Interfaces for more details.
答案2
得分: 3
Bravada Zadada基本上回答了你的问题,但是这里是如何创建一个测试来显示某个操作引起了多少次分配的方法:
func TestMethods(t *testing.T) {
mem := testing.AllocsPerRun(10000000, func() {
_ = Custom{value: "nowhere"}
})
if mem != 0 {
t.Errorf("创建一个对象分配了 %v 内存", mem)
}
}
请参阅testing.AllocsPerRun
文档。你可以使用类似的测试来测试接口、指针方法等等。
英文:
Bravada Zadada basically answered your question, but here's how you make a test that shows how many allocations does a certain action cause:
func TestMethods(t *testing.T) {
mem := testing.AllocsPerRun(10000000, func() {
_ = Custom{value: "nowhere"}
})
if mem != 0 {
t.Errorf("creating an object allocated %v memory", mem)
}
}
See testing.AllocsPerRun
documentation. You can make similar tests with interfaces, pointer methods and what not.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论