Well since YARV and the various GC tunings it's got a lot more heavy actually, and a lot more overhead and so on have landed in the stdlib (e.g. "packaged gems" and so on). This has a lot to do with the fact that the bulk of users for a long time have been running fat applications (e.g. rails).
I still have some suuuuper old campfire daemons around that after many years of uptime are still sitting on a less than 10MB rss. *shrug*
Some final notes:
Rack::Server isn't all that light, it uses option parsers and maps env vars and so on, and creates some garbage. It may also be loading more than it needs to. You could setup a much lighter runtime by using the Handler API directly and configuring things in more memory efficient way (i.e. hardcoded). That's up to you.
I also made a mistake in the above runs, but now can't be bothered to fix it, which is that they're all using the Rack development environment, which is loading middleware you probably don't need in production (e.g. rack/show_exceptions). You'll only really notice the difference if you trim the GC though, as there's more other garbage going on to expand the heap than is evident just avoiding a few more middleware - test it out yourself adding and removing the `-E none` parameter.
I just re-ran with 2.2.3 and it seems to do better in not making excessive allocations:
~rack/rack 2.2.3 % ruby --disable=gems -e 'system "ps -orss= #$$"'
6192
~rack/rack 2.2.3 % ruby -Ilib -rrack -e 'Dir["lib/**/*.rb"].each { |f| begin; require f[%r%lib/(.*)$%, 1]; rescue LoadError; puts "skipped #{f}"; end}; system "ps -orss= #$$"'
skipped lib/rack/session/memcache.rb
skipped lib/rack/handler/thin.rb
skipped lib/rack/handler/lsws.rb
skipped lib/rack/handler/scgi.rb
skipped lib/rack/handler/fastcgi.rb
20400
~rack/rack 2.2.3 % env SCRIPT_NAME=/ HTTP_VERSION=1.0 SERVER_PORT=101010 SERVER_NAME=cgi REQUEST_METHOD=GET ruby --disable=gems -Ilib -rrack -e 'require "rack/server"; Rack::Server.new.start; system "ps -orss= #$$"; p GC.stat' example/
lobster.ru -E none
Status: 200
Content-Length: 592
<title>Lobstericious!</title><pre> ,.---._
,,,, / `,
\\\\ / '\_ ;
|||| /\/``-.__\;'
::::/\/_
{{`-.__.-'(`(^^(^^^(^ 9 `.========='
{{{{{{ { ( ( ( ( (-----:=
{{.-'~~'-.(,(,,(,,,(__6_.'=========.
::::\/\
|||| \/\ ,-'/,
//// \ `` _/ ;
'''' \ ` .'
`---'
</pre><p><a href='?flip=left'>flip!</a></p><p><a href='?flip=crash'>crash!</a></p>11268
{:count=>7, :heap_allocated_pages=>74, :heap_sorted_length=>75, :heap_allocatable_pages=>0, :heap_available_slots=>30163, :heap_live_slots=>29829, :heap_free_slots=>334, :heap_final_slots=>0, :heap_marked_slots=>13743, :heap_swept_slots=>8794, :heap_eden_pages=>74, :heap_tomb_pages=>0, :total_allocated_pages=>74, :total_freed_pages=>0, :total_allocated_objects=>89907, :total_freed_objects=>60078, :malloc_increase_bytes=>712520, :malloc_increase_bytes_limit=>16777216, :minor_gc_count=>4, :major_gc_count=>3, :remembered_wb_unprotected_objects=>346, :remembered_wb_unprotected_objects_limit=>692, :old_objects=>10816, :old_objects_limit=>21634, :oldmalloc_increase_bytes=>1082840, :oldmalloc_increase_bytes_limit=>16777216}
That's only 5MB fully loaded, after serving a request...
You could also consider compiling with / LD_PRELOADING in jemalloc or tcmalloc or whatever, although that doesn't tend to make too much difference in heap size, it's more of a performance concern. Depends a bit on your platform and allocator(s), but there's a possibility these could save you relevant heap space at this level.
... blah ... I'll leave it there for now.
You said you "measured" this and found that Rack was taking 26MB of 31. That doesn't really make sense, can you please provide a reproduction case?