Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why would an empty rails process have different memory footprint each time it starts up?

We've been trying to figure out how to reduce the boot-up memory footprint of our rails app by identifying memory-hungry gems and finding alternatives or solutions.

But there's one behavior on OS X that I find baffling.

With a brand new generated rails app (rails new memoryusage), with no Gemfile, no models, no data and no transactions, upon starting up rails c the memory OSX displays for the corresponding ruby process vill vary each time it is started up, from as low as 60MB to as high as 65MB, with no discernible pattern as to why the same app might be requiring less or more memory per execution.

I imagine this has to do in some way with how Ruby allocates memory, but I'm not completely clear on why its memory allocation would vary so wildly for the same code and no variable processing.

We have similarly unpredictable behavior when we try to calculate the memory consumed by the process after each gem in the Gemfile is required. We load up a vanilla rails process, then in rails c we run a script that parses the Gemfile and requires each Gem individually, logging the memory pre- and post-require, and what we notice is that not only does the memory footprint not have a consistent starting point, but also the incremental 'steps' in our memory consumption vary wildly.

We booted up our process three times, one after the other, and measured the start up memory and the incremental memory required by each gem. Not only did the startup book memory footprints bounce between 60MB and 92MB, but the points at which we saw memory jumps on loading each gem were inconsistent -- sometimes loading SASS would eat up an additional 5MB, sometimes it wouldn't, sometimes active_merchant would demand 10MB additional, others it wouldn't.

                    :    BOOT UP #1            :    BOOT UP #2            :    BOOT UP #3           
gem                 :  increment |      total  :  increment |      total  :  increment |      total
rails               :       0.00 |      59.71  :       0.00 |      92.54  :       0.18 |      67.76
unicorn             :       0.52 |      60.24  :       0.52 |      93.06  :       3.35 |      71.12
haml                :       8.77 |      69.02  :       1.88 |      94.94  :       9.45 |      80.57
haml-rails          :       0.00 |      69.02  :       0.00 |      94.94  :       0.00 |      80.57
sass                :       4.36 |      73.38  :       6.95 |     101.89  :       0.99 |      81.55
mongoid             :       0.00 |      73.38  :       0.00 |     101.89  :       0.00 |      81.55
compass             :      11.56 |      84.93  :       3.23 |     105.12  :       8.41 |      89.96
compass-rails       :       0.00 |      84.93  :       0.08 |     105.20  :       0.00 |      89.96
compass_twitter_bootstrap:       0.00 |      84.93  :       0.00 |     105.20  :       0.00 |      89.96
profanalyzer        :       0.59 |      85.52  :       0.46 |     105.66  :       0.64 |      90.60
simple_form         :       0.34 |      85.87  :       0.35 |     106.01  :       0.00 |      90.60
sorcery             :       0.00 |      85.87  :       0.25 |     106.26  :       1.07 |      91.67
validates_timeliness:       1.47 |      87.34  :       1.82 |     108.07  :       1.62 |      93.29
mongoid_token       :       0.00 |      87.34  :       0.00 |     108.07  :       0.00 |      93.29
nested_form         :       0.00 |      87.34  :       0.00 |     108.07  :       0.01 |      93.30
nokogiri            :       0.86 |      88.20  :       1.16 |     109.24  :       1.37 |      94.67
carmen              :       0.00 |      88.20  :       0.07 |     109.30  :       0.00 |      94.67
carrierwave/mongoid :       2.78 |      90.98  :       0.38 |     109.69  :       0.13 |      94.80
yajl                :       0.04 |      91.02  :       0.04 |     109.73  :       0.04 |      94.84
multi_json          :       0.00 |      91.02  :       0.00 |     109.73  :       0.00 |      94.84
uuid                :       0.00 |      91.03  :       0.00 |     109.73  :       0.41 |      95.25
tilt                :       0.00 |      91.03  :       0.00 |     109.73  :       0.00 |      95.25
dynamic_form        :       0.00 |      91.04  :       0.00 |     109.73  :       0.00 |      95.25
forem               :       0.03 |      91.07  :       0.00 |     109.73  :       0.00 |      95.25
browser             :       0.00 |      91.07  :       0.00 |     109.73  :       0.00 |      95.25
activemerchant      :       2.17 |      93.24  :       1.18 |     110.92  :      10.58 |     105.83
kaminari            :       0.00 |      93.24  :       0.00 |     110.92  :       0.00 |     105.83
merit               :       0.00 |      93.24  :       0.00 |     110.92  :       0.00 |     105.83
memcachier          :       0.00 |      93.24  :       0.00 |     110.92  :       0.00 |     105.83
dalli               :       0.01 |      93.25  :       0.05 |     110.96  :       0.34 |     106.17
bitly               :       2.47 |      95.72  :       9.43 |     120.40  :       1.53 |     107.70
em-synchrony        :       1.00 |      96.72  :       0.18 |     120.57  :       0.55 |     108.24
em-http-request     :       5.56 |     102.28  :       2.15 |     122.72  :       1.40 |     109.64
httparty            :       0.00 |     102.28  :       0.00 |     122.72  :       0.00 |     109.64
rack-block          :       0.00 |     102.28  :       0.00 |     122.72  :       0.00 |     109.64
resque/server       :       1.21 |     103.49  :       1.73 |     124.45  :       1.68 |     111.32
resque_mailer       :       0.00 |     103.49  :       0.00 |     124.45  :       0.00 |     111.32
rack-timeout        :       0.00 |     103.49  :       0.00 |     124.45  :       0.00 |     111.32
chronic             :       1.66 |     105.15  :       0.67 |     125.12  :       0.64 |     111.96
oink                :       0.00 |     105.15  :       0.00 |     125.12  :       0.00 |     111.96
dotenv-rails        :       0.00 |     105.15  :       0.00 |     125.12  :       0.00 |     111.96
jquery-rails        :       0.00 |     105.15  :       0.03 |     125.15  :       0.00 |     111.96
jquery-ui-rails     :       0.00 |     105.15  :       0.00 |     125.15  :       0.00 |     111.96

It's clear to me that there's something very basic that I'm missing and don't understand about how memory is allocated to Ruby processes, but I'm having a hard time figuring out why it could be this seemingly stochastic. Anyone have any thoughts?

like image 599
jfrprr Avatar asked Sep 16 '13 16:09

jfrprr


1 Answers

I'm going to take a wild guess and say this is caused by address space layout randomization and by interactions with shared libraries whose footprints are influenced by running programs that are not in your test case.

OS X has received increasing support for ASLR starting with 10.5 and as of 10.8 even the kernel is randomly relocated.

In some cases, ASLR can cause a program segment to use extra pages depending on whether the offset causes a page boundary to be crossed. Since there are many segments and many libraries, this effect is difficult to predict.

I'm also wondering if (given the huge differences you are seeing) perhaps this is a reporting issue in OS X. I wonder if the overhead for shared objects is being charged unfairly depending on load order.

You can test this by consulting this Stack Overflow question: Disabling ASLR in Mac OS X Snow Leopard.

like image 195
DigitalRoss Avatar answered Nov 10 '22 03:11

DigitalRoss