你正在使用的Go版本是(go version
)?
$ go version
go version go1.13.4 linux/arm
摘要
在linux/arm(或其他32位linux架构)上运行一个简单的golang程序,显示约800MB的虚拟地址空间需要预先保留:
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("pmap", "-x", fmt.Sprint(os.Getpid()))
cmd.Stdout = os.Stdout
cmd.Run()
}
8627: /tmp/testpmap
Address Kbytes RSS Dirty Mode Mapping
00010000 624 444 444 r-x-- testpmap
000b0000 704 176 176 r---- testpmap
00160000 72 36 36 rw--- testpmap
00172000 68 28 28 rw--- [ anon ]
01400000 4096 196 196 rw--- [ anon ]
01800000 524288 0 0 ----- [ anon ]
66cb4000 644 52 52 rw--- [ anon ]
66d55000 264060 0 0 ----- [ anon ]
76f34000 256 32 32 rw--- [ anon ]
76f74000 4 0 0 r-x-- [ anon ]
7ee2c000 132 12 12 rw--- [ stack ]
ffff0000 4 0 0 r-x-- [ anon ]
-------- ------- ------- -------
total kB 794952 976 976
Linux内核可以与三种不同的vmsplit模式一起编译:VMSPLIT_3G, VMSPLIT_2G, VMSPLIT_1G
我有一个使用内存Map文件进行大量内存操作的golang程序,它在ARM设备上运行,供应商已使用VMSPLIT_2G编译其内核。由于golang运行时需要预先保留40%的可用地址空间,因此我的程序在使用其自身目的时受到限制。当地址空间耗尽时,会发生一些糟糕的事情,包括尝试生成线程时的恐慌:
runtime/cgo: pthread_create failed: Resource temporarily unavailable
SIGABRT: abort
PC=0xb68bd6 m=2 sigcode=4294967290
golang运行时在其malloc.go文档中记录了其内存Map。约800MB的预留空间由两个大预留组成:第一个约为258MB,第二个为512MB。
go/src/runtime/malloc.go
第541行至第545行 a23f9af
| | // 1. 我们预先为所有heapArenas保留空间,以便它们不会与堆交错。它们的大小约为258MB,所以这并不是什么大问题。(如果这是个问题的话,我们可以预先保留更少的空间。) |
go/src/runtime/malloc.go
第551行至第552行 a23f9af
| | // 3. 我们尝试占用一个相当大的初始堆预留空间。 |
512MB的初始预留可以通过修补来减少:
go/src/runtime/malloc.go
第586行至第592行 a23f9af
| | arenaSizes:= []uintptr{ |
| | 512<<20, |
| | 256<<20, |
| | 128<<20, |
| | } |
| | for_, arenaSize:=rangearenaSizes { |
| | a, size:=sysReserveAligned(unsafe.Pointer(p), arenaSize, heapArenaBytes) |
然而,我不清楚如何减少约258MB的预留。代码注解中提到“如果这是个问题的话,我们可以预先保留更少的空间”,所以我在寻找一些关于如何做到这一点的指导。
由于32位linux用户空间最多只能访问3GB的内存(当内核使用[默认值] VMSPLIT_3G
编译时),至少可以将258MB的预留减少到约198MB。
4条答案
按热度按时间wd2eg0qa1#
/cc @rsc and @randall77 as runtime owners
I can't quite tell if this is a question, in which case https://golang.org/wiki/Questions may be a faster way for you to get a response (especially from golang-nuts , where it will reach a much broader audience).
kcrjzv8t2#
我认为这可以算作一个bug。简而言之:在32位的Linux系统上,
heapArenaAlloc
在只需要最多198MB的空间时,却预留了258MB的空间。我的主要问题是直接向@aclements提出的关于他的代码注解的问题:
代码注解中提到“如果这是个问题,我们可以提前预留更少的空间”,所以我正在寻求一些关于如何实现这个的建议。
如果我能得到这个问题的答案,我会很高兴地发起一个关闭此问题的CL。
lndjwyie3#
@mknyszek
vyswwuz24#
对不起,我错过了这个。
我认为我的评论是说,它可以为堆竞技场节省更少的空间,并在
mheap_.heapArenaAlloc
用尽时回退到使用新鲜的 mmaps。现在,32位为所有可能的竞技场预留地址空间,只是为了避免将内存预留与堆交织在一起,因为这会导致内存碎片化,使大分配更有可能失败。但在64位上,我们不预先为竞技场预留任何空间,而是根据需要Map它们。
在32位上为区域元数据预留一些空间可能是个好主意,但可能可以少得多。我们还可以尝试生成提示地址,使堆和堆竞技场不太可能发生碰撞。例如,我们可以在初始堆提示之前继续为所有堆竞技场腾出空间,但只是不预留那部分空间。即使没有预留,堆也很可能从提示处增长,不会干扰堆竞技场空间,除非我们的地址空间非常紧张。