Go 语言的内存分配器就借鉴了 TCMalloc 的设计实现高速的内存分配,它的核心理念是使用多级缓存将对象根据大小分类,并按照类别实施不同的分配策略。TCMalloc 相关的信息可以看这里:。
即如果要分配的对象是个小对象(<= 32k),在每个线程中都会有一个无锁的小对象缓存,可以直接高效的无锁的方式进行分配;
如下:对象被分到不同的内存大小组中的链表中。
如果是个大对象(>32k),那么页堆进行分配。如下:
虽然go内存分配器最初是基于tcmalloc的,但是现在已经有了很大的不同。所以上面的一些结构会有些许变化,下面再慢慢絮叨。
因为内存分配的源码比较复杂,为了方便大家调试,所以在进行源码分析之前,先看看是如何断点汇编来进行调试的。
断点调试汇编目前Go语言支持GDB、LLDB和Delve几种调试器。只有Delve是专门为Go语言设计开发的调试工具。而且Delve本身也是采用Go语言开发,对Windows平台也提供了一样的支持。本节我们基于Delve简单解释如何调试Go汇编程序。项目地址:https://github.com/go-delve/delve
安装:
go get github.com/go-delve/delve/cmd/dlv首先编写一个test.go的一个例子:
package main import "fmt" type A struct { test string } func main() { a := new(A) fmt.Println(a) }然后命令行进入包所在目录,然后输入dlv debug命令进入调试:
PS C:\document\code\test_go\src> dlv debug Type 'help' for list of commands.然后可以使用break命令在main包的main方法上设置一个断点:
(dlv) break main.main Breakpoint 1 set at 0x4bd30a for main.main() c:/document/code/test_go/src/test.go:8通过breakpoints查看已经设置的所有断点:
(dlv) breakpoints Breakpoint runtime-fatal-throw at 0x4377e0 for runtime.fatalthrow() c:/software/go/src/runtime/panic.go:1162 (0) Breakpoint unrecovered-panic at 0x437860 for runtime.fatalpanic() c:/software/go/src/runtime/panic.go:1189 (0) print runtime.curg._panic.arg Breakpoint 1 at 0x4bd30a for main.main() c:/document/code/test_go/src/test.go:8 (0)通过continue命令让程序运行到下一个断点处:
(dlv) continue > main.main() c:/document/code/test_go/src/test.go:8 (hits goroutine(1):1 total:1) (PC: 0x4bd30a) 3: import "fmt" 4: 5: type A struct { 6: test string 7: } => 8: func main() { 9: a := new(A) 10: fmt.Println(a) 11: } 12: 13:通过disassemble反汇编命令查看main函数对应的汇编代码:
(dlv) disassemble TEXT main.main(SB) C:/document/code/test_go/src/test.go test.go:8 0x4bd2f0 65488b0c2528000000 mov rcx, qword ptr gs:[0x28] test.go:8 0x4bd2f9 488b8900000000 mov rcx, qword ptr [rcx] test.go:8 0x4bd300 483b6110 cmp rsp, qword ptr [rcx+0x10] test.go:8 0x4bd304 0f8697000000 jbe 0x4bd3a1 => test.go:8 0x4bd30a* 4883ec78 sub rsp, 0x78 test.go:8 0x4bd30e 48896c2470 mov qword ptr [rsp+0x70], rbp test.go:8 0x4bd313 488d6c2470 lea rbp, ptr [rsp+0x70] test.go:9 0x4bd318 488d0581860100 lea rax, ptr [__image_base__+874912] test.go:9 0x4bd31f 48890424 mov qword ptr [rsp], rax test.go:9 0x4bd323 e8e800f5ff call $runtime.newobject test.go:9 0x4bd328 488b442408 mov rax, qword ptr [rsp+0x8] test.go:9 0x4bd32d 4889442430 mov qword ptr [rsp+0x30], rax test.go:10 0x4bd332 4889442440 mov qword ptr [rsp+0x40], rax test.go:10 0x4bd337 0f57c0 xorps xmm0, xmm0 test.go:10 0x4bd33a 0f11442448 movups xmmword ptr [rsp+0x48], xmm0 test.go:10 0x4bd33f 488d442448 lea rax, ptr [rsp+0x48] test.go:10 0x4bd344 4889442438 mov qword ptr [rsp+0x38], rax test.go:10 0x4bd349 8400 test byte ptr [rax], al test.go:10 0x4bd34b 488b4c2440 mov rcx, qword ptr [rsp+0x40] test.go:10 0x4bd350 488d15099f0000 lea rdx, ptr [__image_base__+815712] test.go:10 0x4bd357 4889542448 mov qword ptr [rsp+0x48], rdx test.go:10 0x4bd35c 48894c2450 mov qword ptr [rsp+0x50], rcx test.go:10 0x4bd361 8400 test byte ptr [rax], al test.go:10 0x4bd363 eb00 jmp 0x4bd365 test.go:10 0x4bd365 4889442458 mov qword ptr [rsp+0x58], rax test.go:10 0x4bd36a 48c744246001000000 mov qword ptr [rsp+0x60], 0x1 test.go:10 0x4bd373 48c744246801000000 mov qword ptr [rsp+0x68], 0x1 test.go:10 0x4bd37c 48890424 mov qword ptr [rsp], rax test.go:10 0x4bd380 48c744240801000000 mov qword ptr [rsp+0x8], 0x1 test.go:10 0x4bd389 48c744241001000000 mov qword ptr [rsp+0x10], 0x1 test.go:10 0x4bd392 e869a0ffff call $fmt.Println test.go:11 0x4bd397 488b6c2470 mov rbp, qword ptr [rsp+0x70] test.go:11 0x4bd39c 4883c478 add rsp, 0x78 test.go:11 0x4bd3a0 c3 ret test.go:8 0x4bd3a1 e82a50faff call $runtime.morestack_noctxt .:0 0x4bd3a6 e945ffffff jmp $main.main