title: Why doesn't this Ruby program return off heap memory to the operating system?
body: <p>I am trying to understand when memory allocated off the Ruby heap gets returned to the operating system. I understand that Ruby never returns memory allocated to it's heap but I am still not sure about the behaviour of off heap memory. i.e. those objects that don't fit into a 40 byte RVALUE.</p>
  
  <p>Consider the following program that allocates some large strings and then forces a major GC.</p>
  
  <pre><code>require 'objspace'
  
  STRING_SIZE = 250
  
  def print_stats(msg)
    puts '-------------------'
    puts msg
    puts '-------------------'
    puts "RSS: #{`ps -eo rss,pid | grep #{Process.pid} | grep -v grep | awk '{ print $1,"KB";}'`}"
    puts "HEAP SIZE: #{(GC.stat[:heap_sorted_length] * 408 * 40)/1024} KB"
    puts "SIZE OF ALL OBJECTS: #{ObjectSpace.memsize_of_all/1024} KB"
  end
  
  def run
    print_stats('START WORK')
    @data=[]
    600_000.times do
      @data &lt;&lt;  " "  * STRING_SIZE
    end
    print_stats('END WORK')
    @data=nil
  end
  
  run
  GC.start
  print_stats('AFTER FORCED MAJOR GC')
  </code></pre>
  
  <p>Running this program with Ruby 2.2.3 on MRI it produces the following output. After a forced major GC, the heap size is as expected but RSS has not decreased significantly.</p>
  
  <pre><code>-------------------
  START WORK
  -------------------
  RSS: 7036 KB
  HEAP SIZE: 1195 KB
  SIZE OF ALL OBJECTS: 3172 KB
  -------------------
  END WORK
  -------------------
  RSS: 205660 KB
  HEAP SIZE: 35046 KB
  SIZE OF ALL OBJECTS: 178423 KB
  -------------------
  AFTER FORCED MAJOR GC
  -------------------
  RSS: 164492 KB
  HEAP SIZE: 35046 KB
  SIZE OF ALL OBJECTS: 2484 KB
  </code></pre>
  
  <p>Compare these results to the following results when we allocate one large object instead of many smaller objects.</p>
  
  <pre><code>def run
    print_stats('START WORK')
    @data = " " * STRING_SIZE * 600_000
    print_stats('END WORK')
    @data=nil
  end
  
  -------------------
  START WORK
  -------------------
  RSS: 7072 KB
  HEAP SIZE: 1195 KB
  SIZE OF ALL OBJECTS: 3170 KB
  -------------------
  END WORK
  -------------------
  RSS: 153584 KB
  HEAP SIZE: 1195 KB
  SIZE OF ALL OBJECTS: 149064 KB
  -------------------
  AFTER FORCED MAJOR GC
  -------------------
  RSS: 7096 KB
  HEAP SIZE: 1195 KB
  SIZE OF ALL OBJECTS: 2483 KB
  </code></pre>
  
  <p>Note the final RSS value. We seem to have freed all the memory we allocated for the big string.</p>
  
  <p>I am not sure why the second example releases the memory but the first example doesn't as they are both allocating memory off the Ruby heap. This is one <a href="http://blog.rubybestpractices.com/posts/ewong/005-Avoiding-system-calls.html">reference</a> that could provide an explanation but I would be interested in explanations from others.</p>
  
  <blockquote>
    <p>Releasing memory back to the kernel also has a cost. User space memory
    allocators may hold onto that memory (privately) in the hope it can be
    reused within the same process and not give it back to the kernel for
    use in other processes.</p>
  </blockquote>