Golang中int32变量实际会占用多少内存?

Golang中int32变量实际会占用多少内存? 大家好,

这里有一个困扰我的棘手问题:

i := int32(3)

这段代码将在函数内部声明一个变量。假设该变量会逃逸到堆上,因此需要在堆上分配内存。为了进行完整性检查,我认为在声明该变量后,如果出现以下两种情况之一,它就会逃逸到堆上: j := &i 或者: var j interface{} j = i

现在请听我说,我的 int32 类型的变量 i 是在堆上声明的。这意味着编译器将透明地将我的 i 替换为一个指针,从而在堆上分配一个 int32 变量,同时还有一个指针。如果我说错了,请纠正我。

到目前为止,我们在 32 位处理器机器上已经花费了一个 int32 = 32 位和一个指针 = 32 位。

我还期望在堆上声明的变量可能有一个额外的字段来声明它们的类型。这是真的吗?如果是这样,那么还会再花费 32 位(可能更少)吗?

总之,关于在堆上分配的 int32,我的问题是:

  • 如何测试/查看执行某行代码时分配了多少内存?
  • Go 运行时如何知道我的 int32 实际上是 int32,而不需要为其分配额外的字段?
  • 它是否真的会分配一个额外的字段来指示其类型?
  • Go 运行时是否也会像 Java 那样添加另一个额外的字段作为锁?
  • 除了上述问题之外,我的简单 int32 值是否还会分配其他内存?

请给我推荐一些教程,我相信已经有人研究过这样一个有趣的话题了。


更多关于Golang中int32变量实际会占用多少内存?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

为什么这很重要?

更多关于Golang中int32变量实际会占用多少内存?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我打算弄清楚这行代码会分配多少内存: i := int32(3)

你又忽略了第二个关键点。试着理解它,实践它,让知识融入你的思维。

你不能靠投机取巧成为高级开发者。这是职业自杀的方向。


提示:你已经回答了自己的问题。

这是终极面试问题。 它标志着我从中级Go开发者晋升为高级开发者。 我能更好地思考并理解代码的运行机制。 它有可能通过让我的代码使用更少资源来拯救地球。

我无法更加强调最后一点:对于那些相信气候变化的人来说,环境革命将来自于找到减少浪费实用方法的工程师们。

此外,如果一个人能够理解编程语言的内部工作原理,这展现了高级技术水平。也许像这样的主题会激励年轻开发者选择Golang作为他们的主要语言。

你好 @hollowaykeanho@lutzhorn

感谢你们迄今为止的回复,这些建议让我朝着正确的方向前进了一步。

我使用了 unsafe 来检查 int32 的大小,在我的机器上: 类型:int32 | 地址:0xc00009e000 | 指针大小:8 | 内存大小:4

对我来说,Go 运行时仅为 int32 分配 4 字节似乎几乎不可能。在 Java 中,由于各种透明分配的隐藏字段,这个大小几乎是它的 3 倍。我的意思是,如果没有为所有对象添加额外的(隐藏)字段,怎么可能实现 Go 运行时呢?

我打算准确计算这行代码会分配多少内存:

i := int32(3)

目前,我接下来的两个线索是基准测试和反汇编方法。我会及时更新我的发现。

请随时指出关于这个主题的教程或讨论,因为我自己找不到任何相关内容。Java 有很多这样的教程,包括显示任何库数据类型的额外隐藏字段的图表。

我希望 Go 也有类似的解释和图表,如果没有,我会尽力在这个帖子中发布任何结论。

JOhn_Stuart 说: 我在我的机器上使用 unsafe 检查了 int32 的大小: 类型:int32 | 地址:0xc00009e000 | 指针大小:8 | 内存大小:4 对我来说,Go 运行时仅为 int32 分配 4 个字节似乎几乎不可能。

花点时间分析输出数据。这是数据。32 位是 8 位的 4 倍,当没有底层额外内容时,你为什么会惊讶它分配了 4 字节的内存空间?在 C/C++/嵌入式 C 中,这非常常见。我们甚至有计算内存的习惯(称为"内存计数与跟踪")。

JOhn_Stuart 说: 在 Java 中,由于各种透明分配的隐藏字段,它几乎是三倍之多。我的意思是,如果没有对所有对象的额外(隐藏)字段,如何实现 Go 运行时?

因为 Java 是完全不同的工具。Java 使用 JVM,而 Go 没有。Go 在某种程度上类似于 C/C++,编译为特定于操作系统的机器语言。类似地,Cython、Jython 和 Ruby 由于它们的脚本解释器层而消耗更多内存。

你刚刚经历的是使用 Go 的众多强大原因之一。是时候放下过去,停止将其他工具强加于 Go,并像学习你的第一门编程语言那样吸收 Go。

不能强迫大锤执行铆钉枪的任务,结果会非常糟糕。

在Go语言中,当int32变量逃逸到堆上时,其内存分配情况如下:

1. 内存分配大小

对于逃逸到堆上的int32变量,实际分配的内存通常为:

  • int32值本身:4字节
  • 堆分配的开销:通常8字节(在64位系统上)
  • 总分配:约12-16字节

2. 查看内存分配的方法

使用Go的测试工具和runtime包来测量内存分配:

package main

import (
    "fmt"
    "runtime"
)

func escapeToHeap() *int32 {
    i := int32(3)
    return &i
}

func main() {
    var m1, m2 runtime.MemStats
    
    runtime.ReadMemStats(&m1)
    ptr := escapeToHeap()
    runtime.ReadMemStats(&m2)
    
    fmt.Printf("Allocated: %d bytes\n", m2.TotalAlloc-m1.TotalAlloc)
    fmt.Printf("Value: %d\n", *ptr)
}

使用测试基准来精确测量:

package main

import "testing"

func BenchmarkInt32Allocation(b *testing.B) {
    b.ReportAllocs()
    for i := 0; i < b.N; i++ {
        ptr := escapeToHeap()
        _ = ptr
    }
}

运行:go test -bench . -benchmem

3. 类型信息存储

Go运行时不需要为每个堆分配的对象存储类型信息:

package main

import (
    "fmt"
    "unsafe"
)

type myStruct struct {
    a int32
    b int64
}

func main() {
    var i int32 = 42
    var s myStruct
    
    fmt.Printf("int32 size: %d\n", unsafe.Sizeof(i))
    fmt.Printf("myStruct size: %d\n", unsafe.Sizeof(s))
    
    // 类型信息在编译时确定,运行时通过指针类型知道如何解释内存
    ptr := &i
    fmt.Printf("Pointer type knows it points to int32\n")
}

4. 内存布局示例

package main

import (
    "fmt"
    "unsafe"
)

// 查看逃逸分析
//go:noinline
func createInt32() *int32 {
    i := int32(100)
    return &i
}

func main() {
    ptr := createInt32()
    
    // 查看指针和值的大小
    fmt.Printf("Pointer size: %d bytes\n", unsafe.Sizeof(ptr))
    fmt.Printf("int32 size: %d bytes\n", unsafe.Sizeof(*ptr))
    
    // 实际堆分配可以通过pprof查看
}

5. 运行时行为

Go的堆分配通过runtime.mallocgc处理:

  • 没有每个对象的类型字段
  • 没有内置的锁字段(与Java不同)
  • 类型信息在编译时确定,运行时通过指针类型知晓
  • 垃圾收集器使用位图和其他元数据跟踪对象,而不是在每个对象中存储类型

6. 逃逸分析验证

使用编译命令查看逃逸分析:

go build -gcflags="-m" your_file.go

示例输出会显示哪些变量逃逸到堆上。

推荐资源

  • Go官方文档:runtime包和内存管理
  • 《Go语言设计与实现》相关章节
  • Go源码中的runtime/malloc.goruntime/mbitmap.go
  • Dave Cheney的博客关于Go内存管理的文章

实际内存使用可以通过pprof工具进行详细分析:

go test -memprofile=mem.out -bench .
go tool pprof -alloc_space mem.out

这种设计使得Go在保持类型安全的同时,具有较低的内存开销。

回到顶部