4MB heap arena on darwin/arm64?

461 views
Skip to first unread message

Brad Fitzpatrick

unread,
Jun 9, 2020, 12:57:28 PM6/9/20
to golang-dev
Go's heap arena size is currently 64MB on 64-bit platforms and 32MB on 32-bit and on Windows. From malloc.go:

"We use smaller arenas on Windows because all committed memory is charged to the process, even if it's not touched."

A similar situation exists on darwin/arm64.

I'm using Go in an iOS context that's limited to 16 MB of total memory (dirtied, unpageable memory).

The initial 64 MB virtual memory allocation is fine for a while, and Go's heap is steady at ~2.5MB of allocations.

But over time Go's runtime seems to touch enough of the 64 MB arena to dirty the pages and then iOS kills the process. I can gather stats & graphs if people are interested, but my main question is:

Are there any surprise gotchas if I simply change the arena to be 4MB on darwin/arm64 too?


Austin Clements

unread,
Jun 9, 2020, 4:58:07 PM6/9/20
to Brad Fitzpatrick, golang-dev
I don't think there will be any gotchas. You should be able to do the same thing that windows/amd64 does, which means changing logHeapArenaBytes and arenaL1Bits. I suspect that will just work.

However, I'm not sure it will solve your problem. If the allocator is wandering out into the arena for some reason, then with smaller arenas, it will probably just allocate more arenas. Arenas are just units of heap metadata; the runtime tries to minimize the address space it uses, but there's nothing that specifically pushes back on creating more arenas if the runtime thinks it needs more address space.

Smaller arenas will reduce the metadata overhead, though. If it's actually pages for metadata being faulted in, this might help.

--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-dev/CAFzRk02C0Ch_G%3DoBTWA0iuLHjaX8Mi4aXoW4HBRDX4wM1ergAA%40mail.gmail.com.

Brad Fitzpatrick

unread,
Jun 9, 2020, 5:22:25 PM6/9/20
to Austin Clements, golang-dev
On Tue, Jun 9, 2020 at 1:57 PM Austin Clements <aus...@google.com> wrote:
I don't think there will be any gotchas. You should be able to do the same thing that windows/amd64 does, which means changing logHeapArenaBytes and arenaL1Bits. I suspect that will just work.

Thanks.

I only changed logHeapArenaBytes and it's working so far: https://github.com/tailscale/go/commit/1faebb93a58745174e6b788be8a139776259f6a3

I'll also try changing arenaL1Bits.

However, I'm not sure it will solve your problem. If the allocator is wandering out into the arena for some reason, then with smaller arenas, it will probably just allocate more arenas. Arenas are just units of heap metadata; the runtime tries to minimize the address space it uses, but there's nothing that specifically pushes back on creating more arenas if the runtime thinks it needs more address space.

I see 2-3 arenas now....

ios-free=3.8M Sys=10.9M HeapAlloc=1.7M HeapSys=6.3M HeapIdle=3.6M HeapInuse=2.7M HeapReleased=3.1M StackInuse=1.7M StackSys=1.7M MSpanInuse=0.1M MSpanSys=0.1M MCacheInuse=0.0M MCacheSys=0.0M BuckHashSys=1.4M GCSys=0.8M OtherSys=0.7M NumGC=30
 
Previously with the default 64MB arena I was seeing:

ios-free=0.7M Sys=68.7M HeapAlloc=2.5M HeapSys=62.2M HeapIdle=59.0M HeapInuse=3.3M HeapReleased=58.8M StackInuse=1.7M StackSys=1.7M MSpanInuse=0.1M MSpanSys=0.1M MCacheInuse=0.0M MCacheSys=0.0M BuckHashSys=1.5M GCSys=2.6M OtherSys=0.6M NumGC=561

... notably that's only 0.7MB free, ~3MB less than with 4MB arenas. I'll see if this lasts, though. The other process was up longer.

Smaller arenas will reduce the metadata overhead, though. If it's actually pages for metadata being faulted in, this might help.

I'm not exactly sure what it is.

Just that it's a slow steady decline as reported by os_proc_available_memory (https://developer.apple.com/documentation/os/3191911-os_proc_available_memory) ... "The returned value corresponds to the current memory limit minus the memory footprint of your app at the time of the function call. Your app's memory footprint consists of the data that you allocated in RAM, and that must stay in RAM (or the equivalent) at all times."

With a 64MB arena, I was assuming that different parts of the 64MB address space were being used for different slab class sizes, with a sysUnused on the old parts, but that the resulting madvise MDV_FREE_REUSABLE wasn't enough for accounting reasons for os_proc_available_memory. How hard does the Go allocator try to not dirty parts of the arena that it's never written to before?

I'll report back when I have more numbers.

Thanks again!

cherry

unread,
Jun 9, 2020, 6:32:39 PM6/9/20
to Brad Fitzpatrick, Austin Clements, golang-dev
I'm not really sure about the details, but I just wanted to point out that darwin/arm64 has effectively 33-bit address (https://tip.golang.org/src/runtime/malloc.go#L201), so it seems reasonable to treat it more in line with 32-bit platforms.

Thanks,
Cherry


--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.

li fan

unread,
Jun 4, 2021, 11:08:37 AM6/4/21
to golang-dev
@Brad Fitzpatrick
We meet the same problem: our NetworkExtension process start with the memory occupation of 13M and increase all the time when the goroutines running, after a while, it reach the extension memory limit 15M and be killed by iOS system, can you do me a favor how to solve it, thanks a lot!

Reply all
Reply to author
Forward
0 new messages