go 运行时:在32位Linux上的大型地址空间占用

wgeznvg7  于 6个月前  发布在  Go
关注(0)|答案(4)|浏览(43)

你正在使用的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。

wd2eg0qa

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).

kcrjzv8t

kcrjzv8t2#

我认为这可以算作一个bug。简而言之:在32位的Linux系统上,heapArenaAlloc在只需要最多198MB的空间时,却预留了258MB的空间。
我的主要问题是直接向@aclements提出的关于他的代码注解的问题:
代码注解中提到“如果这是个问题,我们可以提前预留更少的空间”,所以我正在寻求一些关于如何实现这个的建议。
如果我能得到这个问题的答案,我会很高兴地发起一个关闭此问题的CL。

vyswwuz2

vyswwuz24#

对不起,我错过了这个。
我认为我的评论是说,它可以为堆竞技场节省更少的空间,并在 mheap_.heapArenaAlloc 用尽时回退到使用新鲜的 mmaps。
现在,32位为所有可能的竞技场预留地址空间,只是为了避免将内存预留与堆交织在一起,因为这会导致内存碎片化,使大分配更有可能失败。但在64位上,我们不预先为竞技场预留任何空间,而是根据需要Map它们。
在32位上为区域元数据预留一些空间可能是个好主意,但可能可以少得多。我们还可以尝试生成提示地址,使堆和堆竞技场不太可能发生碰撞。例如,我们可以在初始堆提示之前继续为所有堆竞技场腾出空间,但只是不预留那部分空间。即使没有预留,堆也很可能从提示处增长,不会干扰堆竞技场空间,除非我们的地址空间非常紧张。

相关问题