Project

General

Profile

Actions

Misc #19052

open

Increased memory usage (RSS) for Ruby when compiled by gcc

Added by bosh-ecosystem-vmware (BOSH Ecosystem Team @ VMware) about 2 years ago. Updated about 2 years ago.

Status:
Open
Assignee:
-
[ruby-core:110280]

Description

Hello! We have seen a large increase in memory usage as measured by resident set size (RSS) in our Ruby 3.1.2 programs when testing on Ubuntu Jammy (22.04). After further testing, we have narrowed down the problem as starting with Ubuntu Eoan (19.10). We first noticed this increase coming from a gem called eventmachine v1.2.7, which is a concurrency library that uses threads internally. That led us to try threads directly, and we came up with a simple Ruby program that reproduces the memory usage issue by spawning multiple threads through Thread.new.

We are unsure whether the problem extends beyond just Thread.new; it was the easiest problem to spot within our applications.

We are building Ruby from source using ruby-install, though have seen the problem in versions installed via apt.

We are seeking to understand the underlying cause of this memory bloat, and what we can do to prevent it.

Generally, we've been seeing similar total virtual memory allocated, only a difference in RSS usage.

On Ubuntu Disco (19.04) and prior, we see a relatively small increase in RSS upon spawning the threads, on the order of 20 kilobytes per thread. On Ubuntu Eoan and later, we see that RSS increases by 1 megabyte per thread.

When Ruby is compiled using clang instead of gcc on Ubuntu Eoan and later, the memory usage is comparable to older versions of Ubuntu.

We do not believe it is related to gcc version by itself, as we tried compiling Ruby using gcc 9 on Ubuntu Disco, and did not encounter the memory problem. On the theory that it may be a memory fragmentation issue, we tried both using the MALLOC_ARENA_MAX runtime environment variable, and compiling Ruby against jemalloc. Neither of these reduced RSS usage.

Here are the memory differences when simply calling Thread.new 10 times:

Configuration RSS before spawning threads RSS after spawning threads Diff
Disco + gcc 8 13596 kB 13800 kB 204 kB
Disco + gcc 9 13880 kB 14088 kB 208 kB
Eoan + gcc 9 22268 kB 32672 kB 10404 kB
Eoan + clang 13976 kB 14180 kB 204 kB
Jammy + gcc 11 22208 kB 32608 kB 10400 kB
Jammy + clang 13668 kB 13872 kB 204 kB
Jammy + jemalloc 24212 kB 35216 kB 11004 kB
Jammy + apt 21308 kB 31712 kB 10404 kB

We have attached the program and a series of Dockerfiles that replicate the test environments. While the reproduction is using Docker files, we have seen this same behavior on actual VMs. To run a particular Docker config, use ./run.sh <DIRECTORY>, e.g. ./run.sh jammy-clang.

Does anyone understand why there is an increase in resident set size (RSS) when Ruby is compiled with gcc vs clang?
Is there additional configuration (flags, etc) that could be sent to gcc to get equivalent memory usage on Jammy that is seen when using Disco or clang?


Files

memory-test.tgz (1.58 KB) memory-test.tgz bosh-ecosystem-vmware (BOSH Ecosystem Team @ VMware), 10/14/2022 12:14 AM
thread_test.rb (440 Bytes) thread_test.rb bosh-ecosystem-vmware (BOSH Ecosystem Team @ VMware), 10/14/2022 12:15 AM
Actions

Also available in: Atom PDF

Like1
Like0