Memory leak in ActionController::Metal (3.0.7) #1677

Closed
romanbsd opened this Issue Jun 13, 2011 · 11 comments

Comments

Projects
None yet
3 participants
Contributor

romanbsd commented Jun 13, 2011

Consider the following class:

class Myclass < ActionController::Metal
  def myaction
    huge_object.do_something
    # etc.
  end

  private
  def huge_object
    @obj ||= begin
      # allocate and initialize a ~10MB object
    end
  end
end

What I was amazed to find, is that even in production, each time the myaction is called, the object is allocated. Moreover, something is holding a reference to it, as the GC doesn't collect it. I made a workaround by wrapping the object in other class and including Singleton in that class. Still, I want to understand, what's going on here.

Owner

fxn commented Jun 13, 2011

Controller instances are not persistent across requests, you get a new controller instance per request. That's why ivars in a request do not pollute ivars in subsequent requests.

That explains at least why triggering myaction results in a new huge object being allocated even in production mode.

Why the huge object is not gc'ed is a different issue. Can you please provide a minimal app reproducing the problem?

fxn was assigned Jun 13, 2011

Contributor

romanbsd commented Jun 13, 2011

I was under the impression that Metals are converted to Rack app, and persistent, and a #call is used on this instance from now on, I'm probably mistaken. Nevertheless, something is holding a reference.
This is, in fact, a minimal app reproducing the problem. Just create a big object with 'X'_1024_1024*10.

Contributor

romanbsd commented Jun 13, 2011

On line 167 in action_controller/metal.rb it says:
# Return a rack endpoint for the given action. Memoize the endpoint, so
# multiple calls into MyController.action will return the same object
# for the same action.

So either the documentation is incorrect, or the code. So, perhaps, it's two bugs. One is for memoization, and other is for references which are being hold by some objects, creating a memory bloat.

The rack endpoint returned from MyController.action(name) is certainly memoized. But the memoized rack endpoint creates a new instance of the controller class on each call. So both the documentation and the code are correct.

If you need to keep your 10MB around in memory between requests, you will need to figure out a separate way to do it. You cannot use controller instance variables for this, as each controller instance is used for only one request and is then disposed of.

Owner

fxn commented Jun 13, 2011

Exactly, that is the way it works. I cannot reproduce the leak, eventually the garbage collector runs and memory usage drops.

Do you think we can close the ticket?

Contributor

romanbsd commented Jun 13, 2011

It's weird. I'm using ruby 1.9.2, and the GC never sweeps this object. It just adds up. I wonder if something else is causing this. Any idea what might be the culprit?

the GC never sweeps this object

How did you test this? Do you have a script that tests this?

Contributor

romanbsd commented Jun 13, 2011

I did GC.start

Owner

fxn commented Jun 13, 2011

I am running

class LeakController < ActionController::Metal
  include ActionController::Rendering

  def index
    create_huge_object
    render :text => "Hey!\n"
  end

  def create_huge_object
    puts "creating huge object"
    @x ||= begin
      'X'*1024*1024*10
    end
  end
end

in production mode with a loop in a shell

perl -e 'while () { system "curl http://localhost:3000/"; sleep 1}'

Memory usage is around 54M in this mac using 1.9.2.

Contributor

romanbsd commented Jun 13, 2011

Hmm.. possibly something special in my setup then. BTW, I didn't include Rendering module, I was using self.response_body=. Well, if it doesn't reproduce, please close the bug. And sorry for the hassle.

Owner

fxn commented Jun 14, 2011

Tried also self.response_body= without reproducing. Please if you ever reproduce it again feel free to reopen. Thanks.

fxn closed this Jun 14, 2011

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment