Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ask malloc to free kernel pages on GC collection #1072

Open
wants to merge 2 commits into
base: master
from

Conversation

Projects
None yet
3 participants
@samcv
Copy link
Member

samcv commented Mar 20, 2019

This reduces the max memory usage used by asking malloc to free memory
pages. We set it to 128KiB which is glibc's malloc default. This should
hopefully ensure it does get collected.

Credit to:
https://www.joyfulbikeshedding.com/blog/2019-03-14-what-causes-ruby-memory-bloat.html

PS: just want to say this isn't ready for merge yet, as not all libc's (though most) have malloc_trim

@AlexDaniel

This comment has been minimized.

Copy link
Member

AlexDaniel commented Mar 23, 2019

So, is there any before↔after comparison?

@samcv

This comment has been minimized.

Copy link
Member Author

samcv commented Mar 23, 2019

@AlexDaniel
I
go from 8.7-8.2 max to 6.3-7.2 with my script that cats a linux iso 5 times doing malloc_trim(128*1024);

@samcv

This comment has been minimized.

@AlexDaniel

This comment has been minimized.

Copy link
Member

AlexDaniel commented Mar 23, 2019

OK, nice! But why 128KiB?

621 The `pad' argument to malloc_trim represents the amount of free
622 trailing space to leave untrimmed. If this argument is zero,
623 only the minimum amount of memory to maintain internal data
624 structures will be left (one page or less). Non-zero arguments
625 can be supplied to maintain enough trailing space to service
626 future expected allocations without having to re-obtain memory
627 from the system.

Given how memory-hungry rakudo is, wouldn't it be better to have that argument much larger (probably in megabytes)?

@AlexDaniel

This comment has been minimized.

Copy link
Member

AlexDaniel commented Mar 23, 2019

Not sure how to benchmark it, maybe a loop that creates a lot of objects that are thrown away relatively quickly? I'm guessing it'd be a bit faster with higher pad sizes.

@samcv

This comment has been minimized.

Copy link
Member Author

samcv commented Mar 24, 2019

@AlexDaniel the size used is the default. So no real reason why it should be that size. But the conditions that it releases memory to the kernel are not guaranteed to run with any regularity.

@AlexDaniel

This comment has been minimized.

Copy link
Member

AlexDaniel commented Mar 25, 2019

A GNU extension, called malloc_trim(), exists for releasing memory from the top of the heap, but it can be painfully slow. It hurts real bad for a lot of small objects, so it should be used sparingly.

source

See also: https://dev.to/evilmartians/cables-vs-malloctrim-or-yet-another-ruby-memory-usage-benchmark-3emo (TL;DR in some cases there's a performance hit)

How often do we run gc? Do we actually need to run malloc_trim as often?

We really need a way to benchmark this…

samcv added some commits Mar 20, 2019

Ask malloc to free kernel pages on GC collection
This reduces the max memory usage used by asking malloc to free memory
pages. We set it to 128KiB which is glibc's malloc default. This should
hopefully ensure it does get collected.

Credit to:
https://www.joyfulbikeshedding.com/blog/2019-03-14-what-causes-ruby-memory-bloat.html

@samcv samcv force-pushed the samcv:malloc_trim branch from 3baa624 to 8c2b0bd Mar 26, 2019

@Kaiepi

This comment has been minimized.

Copy link
Contributor

Kaiepi commented Mar 28, 2019

There is no malloc_trim on *BSD.
On OpenBSD, you could use freezero to explicitly ensure memory is freed.
FreeBSD and NetBSD use jemalloc. I'll take a look at how it works tomorrow and see if I can find any way to solve this for those OSes

Edit: OpenBSD frees memory pages when needed. I'll need to test freezero more to see if it actually fixes the problems with running out of memory or not.

@Kaiepi

This comment has been minimized.

Copy link
Contributor

Kaiepi commented Mar 28, 2019

freezero appears to fix the leak with the test code in the Rakudo issue when I build moar to use it in src/io/asyncsocket.c and src/strings/utf8.c:

bastille% perl6 test.p6
153.445313MiB maxrss – before doing anything!!
「4b32fcab2192054b82312cde4c235adda0b8ac4d.zst」
186.664063MiB maxrss 
「eb3123e5e60fa1635ed1ee121cec1290c290044a.zst」
203.773438MiB maxrss 
「f66f8be09c6023b3b53e32f7571ea536ad87b87f.zst」
212.054688MiB maxrss 
「b2a3441749878e338b0861b14b3b9433cc902f42.zst」
219.449219MiB maxrss 
「78980ed447cceff82f5efef16dbe9ee437aae809.zst」
232.742188MiB maxrss 
「aa94ffc33712c6f3068c6472b79a74fa70aa7b2e.zst」
232.742188MiB maxrss 
「a0a28432f54d608130f17247f9202f4c4939dfff.zst」
232.742188MiB maxrss 

A couple problems though:

  • It can't be used everywhere in MoarVM currently since it requires a size argument, making it impossible to use with void * variables unless you already know what type they were before they were cast
  • Where would it be appropriate to use free instead of freezero? Not zeroing out the memory with freezero is supposed to be useful in cases where the same memory gets allocated again.

This part of the fix might be better off in a separate pullreq.

@Kaiepi

This comment has been minimized.

Copy link
Contributor

Kaiepi commented Mar 30, 2019

Sorry, should've commented on the Rakudo issue, not here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.