1

I'm trying to figure out an issue with memory usage in a ruby process. I tried to take a heap dump of the ruby process using the ObjectSpace module to understand what's happening. What's puzzling is that, the "top" command in linux reports that the process uses 17.8 GB of virtual memory and 15GB of resident memory. But, the size of the heap dumps are only around 2.7-2.9 GB.

Based on the Ruby documentation, Objectspace.dump_all method dumps the contents of the ruby heap as JSON.

I'm not able to understand what is hogging the rest of the memory. It would be helpful, if someone could help me to understand what's happening.

Thank you.

user2492286
  • 99
  • 10
  • 1
    [Tenderlove's talk at the Rubyconf.au](https://www.youtube.com/watch?v=nAEt36XNtAE) this year might be helpful. – spickermann Jul 24 '17 at 17:21

1 Answers1

1

It is likely that your application is allocating objects that are then groomed by the Garbage Collector. You can check this with a call to GC.stat

Ruby does not release memory back to the operating system in any meaningful way. (if you're running MRI) Consequently, if you allocate 18GB of memory and 15GB gets garbage collected, you'll end up with your ~3GB of heap data.

The Ruby MRI GC is not a compacting garbage collector, so as long as there is any data in the heap the heap will not be released. This leads to memory fragmentation and the values that you see in your app.

anothermh
  • 9,815
  • 3
  • 33
  • 52
  • I did read the article and understood that Ruby gradually releases the memory to the OS over a period of time. I did measure the available heap slots periodically and it was constantly around 26 million. I read that each slot is 40 bytes in a 64 bit machine. So, by doing the math it should be 26 Million * 40 bytes ~ 991 MB. Heap dump size is 2.9 GB and process's resident memory size is ~15 GB. Looks like I'm still missing the connection – user2492286 Jul 24 '17 at 21:17
  • 1
    [Ruby doesn't store everything in the heap.](https://stackoverflow.com/a/13639298/3784008) Objects whose size exceeds the heap are allocated memory outside the heap. Unless all of your object allocations are < 40 bytes, Ruby is allocating memory to hold them. (using malloc, further abstracting the memory layer and thus being dependent on the operating system) – anothermh Jul 24 '17 at 21:29
  • Thank you for the valuable information. It was helpful to understand how ruby manages memory. – user2492286 Jul 25 '17 at 00:07
  • Is there a way to analyze what's occupying the remaining memory? Does the memsize field in the heap dump report only the heap size used or does it account for the memory used by the object outside the heap? – user2492286 Jul 27 '17 at 17:19
  • I'm not sure, but a tool like [heapy](https://github.com/schneems/heapy) and this [two](https://blog.codeship.com/the-definitive-guide-to-ruby-heap-dumps-part-i/) [part](https://blog.codeship.com/the-definitive-guide-to-ruby-heap-dumps-part-ii/) article series from its author may help you find the answer. – anothermh Jul 27 '17 at 17:28
  • "[It is] stored on the System Heap. The Slot in the Ruby Heap simply contains a reference to that memory location on the System Heap which contains the 50MB of data." [Reference](http://www.theirishpenguin.com/2009/10/29/understanding-how-ruby-stores-objects-in-memory-the-ruby-heap.html) – anothermh Jul 27 '17 at 20:08
  • @user2492286 [This article](http://patshaughnessy.net/2012/1/4/never-create-ruby-strings-longer-than-23-characters) goes into some more detail about the topic. – anothermh Aug 09 '17 at 00:44
  • Thank you for all the links and info. I'm still investigating the root cause of the memory bloat. I'll update here, if I find anything interesting. – user2492286 Aug 09 '17 at 05:09