Live data from Hacker News

How I spent two weeks hunting a memory leak in Ruby (2015)

be9.io

61–70 of 71 posts

Re: How I spent two weeks hunting a memory leak in Ruby (2015)

#61

Interesting post, but I did want to chime in quickly and say that it's pretty absurd to only have 1GB for a web application in 2016, even for a small one. This is why I've been dismissive of using Heroku for my projects, even though I run a lean stack. They have higher RAM options, but they are incredibly expensive. For the cost of a 14GB Heroku Dyno, I can buy a dedicated server off ebay with 32GB ECC every single m…

> it's pretty absurd to only have 1GB for a web application in 2016 While I don't for a minute doubt what you say, as an old guy, the absurdity of 1GB being insufficient for serving up web pages hits me pretty hard.

For static sites, surely just load all of the content into memory and serve it based on key/index? eg. load into a C++ map of , where first is URL and second is content. Even lookups would be fast (and would negate the need to hit the disk too).

For dynamic stuff, adjust accordingly.

Re: How I spent two weeks hunting a memory leak in Ruby (2015)

#62

Interesting post, but I did want to chime in quickly and say that it's pretty absurd to only have 1GB for a web application in 2016, even for a small one. This is why I've been dismissive of using Heroku for my projects, even though I run a lean stack. They have higher RAM options, but they are incredibly expensive. For the cost of a 14GB Heroku Dyno, I can buy a dedicated server off ebay with 32GB ECC every single m…

> it's pretty absurd to only have 1GB for a web application in 2016, even for a small one

what is pretty absurd is the amount of memory a ruby web application uses. 1GB should be enough in 2016 to serve a small web app. Ruby might be a pleasant language to write it was never designed to be efficient memory wise. And frameworks like Rails that have 0 concerns for real performances make things even worse. That's why people are ditching Ruby for statically typed alternatives, constantly. It's just not a good language for the task in 2016.

Re: How I spent two weeks hunting a memory leak in Ruby (2015)

#63
post #29

Earlier quoted context omitted.

There is no xmalloc in the libc.

Okay, interesting. I guess I've seen it before, so I didn't think it was Ruby-specific, but I also haven't had to do that kind of programming in a very super long time. Publib then? I'm finding this when I search: http://man.cx/xfree(3) http://man.cx/publib(3) Also, are you sure? I see xmalloc in glibc all over the internet...

It's fairly common to define malloc and free wrappers called xmalloc and xfree. The usual idiomatic thing to do is to have xmalloc check the return value of malloc and terminate the program if it ever fails, though this is traditionally done in application code rather than a library. I doubt libc would ever add an xmalloc function because it'd break all the code that defined its own xmalloc.

Re: How I spent two weeks hunting a memory leak in Ruby (2015)

#64

Earlier quoted context omitted.

> it's pretty absurd to only have 1GB for a web application in 2016 While I don't for a minute doubt what you say, as an old guy, the absurdity of 1GB being insufficient for serving up web pages hits me pretty hard.

Our main proxies (that do the majority of the heavy lifting over here) run nginx and use, what, 50MB tops? If even? I throw them on $5/mo VPS instances and they just blast out IO without any problems. But nginx was carefully written in C over the course of many years, with a general goal of hauling IO and static data around from other systems. Which is all you really need for web pages, in the end. Solved one thing a…

If you don't want to wait for Crystal to mature there's a long line of high-level languages with similar performance characteristics - check out OCaml or Haskell, or perhaps F# or Scala (if it's throughput you need rather than fast startup).

Re: How I spent two weeks hunting a memory leak in Ruby (2015)

#65

Earlier quoted context omitted.

> it's pretty absurd to only have 1GB for a web application in 2016 While I don't for a minute doubt what you say, as an old guy, the absurdity of 1GB being insufficient for serving up web pages hits me pretty hard.

For static sites, surely just load all of the content into memory and serve it based on key/index? eg. load into a C++ map of , where first is URL and second is content. Even lookups would be fast (and would negate the need to hit the disk too). For dynamic stuff, adjust accordingly.

You don't even have to bother loading it in memory. Just use `sendfile(2)` and let the kernel do all the work for you.

You can't really do faster than that.

Re: How I spent two weeks hunting a memory leak in Ruby (2015)

#66
post #37

Earlier quoted context omitted.

Interpreted languages can have pretty high memory overhead. Let's say we have a page in python or php or ruby or whatever, and it needs to fetch 1,000 rows from a database (100 things here, 20 things there, whatever). Each row in the database can be turned into an object several times, one from the model, one from the controller, one for the template/view. On top of that, each object is a composite key/value object o…

Needing to fetch 1k rows from a database is different from needing to keep all that data in memory, however. Several years ago I had to return 1M rows from a database to the client with a PHP script, and it needed less than the 16MB (yes, sixteen megabytes ) that the environment of the time was configured with as a memory limit. 1G rows would not have consumed any more memory either, because at any one time the scrip…

And what do you do if an error happen in the middle of your iteration while the server already answered with 200 OK and sent half of the response?

Your only option in those case with PHP is to truncate the response which IMO is the ridiculous choice here...

Re: How I spent two weeks hunting a memory leak in Ruby (2015)

#67

Earlier quoted context omitted.

The shortest code you can get away with is not always the best. If the parameter is a structure (which I think it is here, behind the void pointer), it's a good idea to give it its own free function in case later you add additional struct members that will need freeing/cleanup in the same place. (Bonus points if it appears in roughly the same place this thing is malloc'd) I would have been even more explicit and adde…

That sounds like premature generalisation to me. I'd say use a separate free function when it needs one , which is not the case yet (and might never be.)

I think this is more of a maintainability/readability case. Your suggestion sounds like premature optimization to me.

Re: How I spent two weeks hunting a memory leak in Ruby (2015)

#68
I had similar experience of finding memory leak in Python. It's a 64 G server, but we load everything from database to memory. I didn't success.

This experience made me question myself: is it wrong to create a lot of objects in memory and let them reference each other, and hope Garbage Collector to magically work?

If instead of object, we just put data into memory more organized as a (column) database, then we don't need GC anymore.

OOP made programming easier, by modeling real world as object. At the same time, it made memory management harder, since it's not nature to the hardware's memory model: a linear array of memory cells.

I wish in future, there is a place for this paradigm of in memory database model of programming.

Re: How I spent two weeks hunting a memory leak in Ruby (2015)

#69

What a timely post. I had a similar issue that I was tracing last week that did end up being my Ruby code...and it turns out I was modifying a constant like in the example. What a fun read! I've been learning more about Ruby's GC since 2.1 and this got me looking even deeper -- definitely picked up a couple of new tricks/tools from this. Thank you be9!

Freeze your constants!

I used to do this with strings (now you don't have to), but this is great advice!

Re: How I spent two weeks hunting a memory leak in Ruby (2015)

#70

Earlier quoted context omitted.

Freeze your constants!

I used to do this with strings (now you don't have to), but this is great advice!

Yeah, it's one of those rare Ruby gotchas. If you assign some kind of a data structure to a constant, you'll get a warning if you try to reassign the constant, but the data structure itself will silently mutable. To disallow this behavior, simply freeze the data structure. If you have nested data structures, you must freeze deeply.

  ENUM = [0, 1, 2].freeze

  LOOKUP = {
    :A => [1, 2, 3].freeze,
    :B => [4, 5, 6].freeze
  }.freeze

  # or:

  LOOKUP = {
    :A => [1, 2, 3],
    :B => [4, 5, 6]
  }.tap { |s| s.values.each(&:freeze) }.freeze
Post reply on HN