Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OutOfMemoryError instead of Exceptions #216

Closed
Hakon opened this issue Jun 27, 2012 · 23 comments
Closed

OutOfMemoryError instead of Exceptions #216

Hakon opened this issue Jun 27, 2012 · 23 comments
Milestone

Comments

@Hakon
Copy link

Hakon commented Jun 27, 2012

Hello, headius suggested i'd file a bug on this.

Sometimes when my code throws an exception instead it sits for over a minute and then throws an OutOfMemoryError.
This example trace is a ActionView::Template::Error but i guess ActionView catches the error and throws its own.

ActionView::Template::Error (Java heap space):
  java.nio.HeapCharBuffer.<init>(HeapCharBuffer.java:39)
  java.nio.CharBuffer.allocate(CharBuffer.java:312)
  java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:775)
  java.nio.charset.Charset.decode(Charset.java:771)
  org.jruby.RubyEncoding$UTF8Coder.decode(RubyEncoding.java:246)
  org.jruby.RubyEncoding.decodeUTF8(RubyEncoding.java:192)
  org.jruby.RubyString.decodeString(RubyString.java:719)
  org.jruby.RubyString.toString(RubyString.java:701)
  org.jruby.RubyNameError$RubyNameErrorMessage.to_str(RubyNameError.java:120)
  org.jruby.RubyNameError$RubyNameErrorMessage$INVOKER$i$0$0$to_str.call(RubyNameError$RubyNameErrorMessage$INVOKER$i$0$0$to_str.gen)
  org.jruby.RubyClass.finvoke(RubyClass.java:679)
  org.jruby.javasupport.util.RuntimeHelpers.invoke(RuntimeHelpers.java:541)
  org.jruby.RubyBasicObject.callMethod(RubyBasicObject.java:364)
  org.jruby.util.TypeConverter.convertToType(TypeConverter.java:71)
  org.jruby.util.TypeConverter.convertToType(TypeConverter.java:117)
  org.jruby.RubyBasicObject.convertToString(RubyBasicObject.java:758)
  org.jruby.RubyNameError.to_s(RubyNameError.java:202)
  org.jruby.RubyNameError$INVOKER$i$0$0$to_s.call(RubyNameError$INVOKER$i$0$0$to_s.gen)
  org.jruby.RubyClass.finvoke(RubyClass.java:679)
  org.jruby.javasupport.util.RuntimeHelpers.invoke(RuntimeHelpers.java:541)
  org.jruby.RubyBasicObject.callMethod(RubyBasicObject.java:364)
  org.jruby.RubyException.message(RubyException.java:266)
  org.jruby.RubyException$INVOKER$i$0$0$message.call(RubyException$INVOKER$i$0$0$message.gen)
  org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:292)
  org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:135)
  org.jruby.ast.CallNoArgNode.interpret(CallNoArgNode.java:63)
  org.jruby.evaluator.ASTInterpreter.setupArgs(ASTInterpreter.java:357)
  org.jruby.ast.SuperNode.interpret(SuperNode.java:107)
  org.jruby.ast.NewlineNode.interpret(NewlineNode.java:104)
  org.jruby.ast.BlockNode.interpret(BlockNode.java:71)
  org.jruby.evaluator.ASTInterpreter.INTERPRET_METHOD(ASTInterpreter.java:75)
  org.jruby.internal.runtime.methods.InterpretedMethod.call(InterpretedMethod.java:298)
@sgonyea
Copy link

sgonyea commented Jul 6, 2012

Have any code to reproduce this? What do your JRUBY_OPTS and JAVA_OPTS look like?

@headius
Copy link
Member

headius commented Nov 6, 2012

I believe this is related to a case where AR will attempt (during exception raising) to to_s a model or query object (or something) that has an extremely large amount of data backing it up. The result is that it consumes too much memory and never gets to the point of raising the actual error.

@BanzaiMan
Copy link
Member

I am not sure if we can solve this one. Perhaps giving more memory could help?

I'm closing this for now; if there is a reproduction and any fabulous idea to go on with, we'll revisit.

@mmustala
Copy link

mmustala commented Apr 1, 2015

I'm seeing this same issue in my code right now. My code is handling images and other biggish files and it is quite common that when an exception is encountered the process consumes all the heap it has (3 Gigabytes currently) and then the Java heap space is thrown.

I very much suspect the same as headius. What about instead of just to_s on the object we could take only the first 1000 characters or something.

@sgonyea
Copy link

sgonyea commented Apr 1, 2015

@mmustala: I'll drop this in the "ideas bucket." Consider me a crank until @headius weighs in:

Far from an ideal solution, but for objects that can become this large... I'd perhaps suggest monkey patching #to_s and using ObjectSpace.memsize_of(self) (require 'objspace' elsewhere). If it's very, very large then spit out a custom string and perhaps log the source of the issue.

For objects that can become really huge, overriding the behavior of #inspect is probably your only reliable course of action. Overriding methods is pretty standard once you start working with abnormally large blobs of data.

@headius
Copy link
Member

headius commented Apr 6, 2015

I'm guessing this is actually to blame for the memory explosion on exception raise: https://bugs.ruby-lang.org/issues/9725

Not really sure how to avoid this; it's a weird misfeature of Ruby that it inspects the target of a NameError in this way. Maybe anyone affected by this should +1 the issue I filed.

@headius headius reopened this Apr 23, 2015
@headius
Copy link
Member

headius commented Apr 23, 2015

MRI has made no movement on the issue I filed, so I feel like we need to fix this unilaterally.

We can mitigate this problem by catching OutOfMemoryError in the NameError logic and falling back on a simple class name. That could capture OOME unrelated to the string, but it's very unlikely (and if it's really OOM, another one will come along shortly).

Beyond that, I think we should just modify NameError to not do this. @enebo?

@JamesIry
Copy link

The problem with catching OOMError is it's inherently unreliable. If thread A is doing something insane (like building a ginormous string for an exception) and then thread B does a reasonable allocation at the wrong time then B will get an OOM and probably die a horrible death.

I think your discussion on the MRI bug is spot on: you have to prevent the ginormous string allocation in the first place. Detecting the OOM after-the-fact will lead to even harder to diagnose problems, albeit more rarely.

@sgonyea
Copy link

sgonyea commented Apr 23, 2015

Hard to say if MRI would accept this change, since it would have dual behavior across Error objects. It helps that in the land of MRI, memory swap is freeeee! :trollface:

You should definitely solve this unilaterally. If it doesn't impact performance too hard, you could even juggle behavior based on the Obj's size... Or swap in a helpful String to let them know why there's no inspect output.

@headius
Copy link
Member

headius commented Mar 24, 2016

There's no bug here for us to fix...it's an unfortunate side effect of standard Ruby behavior.

JRuby 9.1 will not limit memory to 500MB, so perhaps it will rarely run into a NameError that's too big.

If you would like to see this changed, add your vote to the CRuby bug: https://bugs.ruby-lang.org/issues/9725

@headius headius closed this as completed Mar 24, 2016
eregon added a commit that referenced this issue Mar 28, 2016
f5d943e A few cosmetic fixes in Thread#backtrace spec
38cc7ef Basic tests of Thread#backtrace
c062803 Test where class variables are defined and updated
9f94d6c Add a spec for adding two invalid strings together that results in a valid one
86ddccf Fixed Symbol.all_symbols spec.
a98b62d Wait for a thread status change rather than a random amount of time in rb_thread_blocking_region
3dba3bf Merge pull request #219 from donv/basic_coverage_specs
2e7722e adapt realpath specs to run on Windows
6f1fbd4 syslog is POSIX-only
d9b2e19 adapt realdirpath specs to run on Windows
e80ca16 Process.daemon is not implemented on Windows
801850b Guard unicode path spec not working on Windows
b6c738b Dir.home with an argument is not working on Windows
aa55259 Update for changes in mspec's home_directory helper
25e443c Removing problematic path in dir specs for Windows
8f3e280 Show remaining files if delete_mock_dirs fails
83a11ee Merge pull request #216 from ruby/vais/windows
53b800f Skip all specs that rely on fork on Windows
76811e1 Wrap a few remaining fork-using specs in with_feature :fork block
937be98 Use explicit receiver for the helper method
afbcef4 Change namespace odule to CoverageSpecs to avoid clashing with stdlib
0ecdf4d Guarded specs for peek_result for Ruby 2.3 Used raise_error helper instead of customer begin...rescue
da7b44b Added specs for peek_result Pulled helper method to separate SpecHelper module
607e7ea Filter old results from result hash
3f65bbd Added specs for multiple runs
c3e050f Added a basic spec for counts
490cc2d Merge pull request #217 from donv/try_add_coverage_spec
dbaf24f Started adding spec for coverage
0f2743d Merge pull request #218 from donv/test_mri_2.3
86f19a1 Start testing MRI 2.3.x
07ea93b Fix indent
7e241f7 Fix typo
1a2d168 Reasonable precision in Bignum#* spec
342f9ae Merge pull request #215 from ruby/vais/windows
854cd89 Run AppVeyor only on master and try* branches
fe5acba Passing all WIN32OLE specs
e36ff82 Merge pull request #214 from ruby/vais/windows
4575e16 Skip the entire ARGF.read_nonblock spec on Windows
f580a29 Merge pull request #212 from wied03/master
323d258 Cover another alias/super case
504eea0 Revert "Clean up the entire spec temp dir when finished with mock dirs."
8d75627 AppVeyor: use an inclusion filter
182a683 AppVeyor: fix last commit
4f24a03 AppVeyor: exclude C-API specs for now
40411ed AppVeyor: use the file formatter
ea7d4ba Remove branch filter in appveyor.yml
bfa4a13 Make sure the Signalizer is not run on Windows since it hangs
3bbc6ae Try to run the LeakChecker under AppVeyor
a9c496a Add setup for AppVeyor
06e8a89 Merge pull request #210 from odaira/myContribution
9c17cb6 Merge pull request #211 from ruby/vais/windows
8b5c3eb Pass ENV specs on Windows
2307c18 Remove extra File.join in DirSpecs.nonexistent
eaaf635 Module#{include,prepend} are public since 2.1
b144eac Remove 'ruby_version_is ""..."2.1" do' guards since 2.1 is now the lowest version
32c5020 Remove 'ruby_version_is "2.1" do' guards since this is now the lowest version
c0b7368 Modify a test that succeeds or fails depending on today's date in a year. Time#gmtoff values match only when both or neither of the Time objects are in summer time
2458625 Drop support for 2.0.0 as it is no longer supported
6d4e6f4 Merge pull request #208 from ruby/vais/argf
52eff83 Pass ARGF.binmode on Windows
e8c46eb Fix leak in Kernel#open
5080525 Choose a more appropriate error class in C-API thread specs
c55e620 Fix 2 thread leaks in C-API specs
0c65308 Terminate the WEBrick timeout thread when supported
6d0be63 Fix thread leak in Thread#name spec
ddf7d41 Show leaked subprocess pid
c7a99a6 Revert "Remove now useless Process.waitall"
9676480 Fix subprocess leaks in ObjectSpace.define_finalizer specs
3229895 Do not leak a Dir in Marshal#load specs
4ff6dd5 Fix fd leak in Dir#fileno setup
32c48c7 Remove now useless Process.waitall
4d73980 Wait for subprocess to die in Process specs
b513748 Close the pipe reading-side in the parent as well to avoid leaking a fd
a277946 Unshare Process.times since there is only one usage
c67efe7 Merge pull request #205 from unak/patch-4
ff2c878 get rid of blocking on Windows
5a1ea16 Merge pull request #204 from unak/patch-3
7a2f6b0 Should not assume to exist outer commands.
3b52dae Merge pull request #203 from unak/patch-2
87898e6 Wrong assumption
adbe6d5 Make STDIN non-EOF
add86e1 Revert "Remove problematic specs"
8b5cb71 Remove problematic specs
265cc4c fixup! remove unguaranteed behavior
d8aadfb remove unguaranteed behavior
56d2e70 remove rb_class_inherited spec
4056f12 Remove unreliable spec
01bad6a Do not spec Unicode changing behavior
2258d1d Add missing 2.1 version guards.
3a80eb8 Add missing 2.3 version guards
8825b5f Add a spec for autoloading a constant that was already loaded by another thread
9be9a58 ObjectSpace._id2ref raises for invalid ID values
9d7f072 Move String#@{+,-} specs to the right files
a579440 Added String#-@ and String#+@
974a254 Remove redundant specs
6a44638 Added NameError#receiver spec
e976989 Add specs for rb_time_timespec_new
1b4d6a2 Add specs for rb_define_class_id_under
d61a24c Add a spec for rb_define_class_under when given a mismatched superclass
4e64448 add example which specs behavior of Proc#for_define_method when procs @ruby_method is set
cba082b Added local variable methods to Binding
3c5e63d Use the argf helper in ARGF.read_nonblock
0938149 Added specs for ARGF.read_nonblock
b960ebe Spec Enumerable#sort_by when #map does not return an array
51b21d0 Use describe, rephrase spec description
523f780 Fix count when enumerable yields multiple values
62c1c8c Reword spec description
309fda6 Add a spec covering Array#product returning self when given a block
d9bbeb1 Add another failing specs for zsuper with keyword arguments
89e8773 Fix Kernel#rand when given a Float exclusive range.
08dd725 Added failing specs for zsuper with rest kwargs
a828e0d Add failing specs for slice_* when iterator method is changed
7bb5997 Add specs for IO#{read,write}_nonblock accepting exception option
51fe64d Add specs for ARGF.readpartial
f5b730c Add a spec covering clearing output buffer of ARGF.read
214cf9e add failing spec for String.new with optional encoding argument
48fd956 Move Hash comparison specs to the right files
7ed5840 Avoid the new_hash helper
9c43b34 Add specs for Hash comparison operators
4f7eaa5 Fixed some 64bit assumptions in specs.
1e84f38 Fix a typo in Enumerator::Lazy specs
48060b6 Add specs for File.mkfifo
1e971da Add specs for #dig with no arguments
59b2043 Add specs for Enumerator::Lazy#grep_v
38801cd Add specs for Hash#to_proc
93c40ee Add a spec for setting thread's group in rb_thread_create
668056f Add a spec covering rb_rescue returning the break value when the passed function yields to a block with a break
34bf4b5 Added 2.3 specs for #positive? and #negative?
3988aac multiple calls of close, close_read, and close_write should no longer raise IOError
065e422 Add specs for #dig implmenetation for Hash, Array and Struct classes
0be9395 puts: Make it more compatible to MRI
e3c3858 spec 2.2 behavior where limit 0 raises ArgumentError
d4f49e6 puts: Check if argument responds to :to_ary, instead of checking its type being Array
cc7a472 Add spec for Enumerable#first with multiple yield parameters
7ca9596 Automatically run Travis on try* branches
ba0bd69 Merge pull request #202 from wied03/master
f6ed69a NoMethodError should not require a name (because underlying NameError class does not)
81977c9 sprintf: test infinite and nan float values
37d0d3c Merge pull request #201 from nobu/no-new_ostruct_member
c800735 Do not test method for internal use

git-subtree-dir: spec/ruby
git-subtree-split: f5d943e19e980264de972b6d1b49c927e527053b
@prathamesh-sonpatki
Copy link
Contributor

prathamesh-sonpatki commented Apr 22, 2016

@headius Any suggestions for getting around this issue? Looks like we are facing this with JRuby 9.0.4.0 and Sidekiq.

@mmustala
Copy link

I didn't find anything else but overriding the inspectmethod in the class that is causing the problem. That way you can make sure inspect is returning a string that is not too big.

@prathamesh-sonpatki
Copy link
Contributor

@keithrbennett
Copy link

I'm going to weigh in on this...I don't have any direct experience with this bug but I have been conversing with someone on Stack Overflow who does -- he gave up using JRuby, even though he needed to call Java libraries and it was the obvious choice, because of this problem (see comments in http://stackoverflow.com/questions/37316055/file-write-happening-asynchronously/37317141?noredirect=1#comment62158497_37317141).

On the higher level, my opinion is that class authors should not presume to know how to_s and inspect are called, and support their use by the (Ruby or Java) VM and frameworks out of their field of influence by ensuring that the size of the strings they return is reasonable (maybe <= 1K?), and that the cost of calling them is low (e.g. not to read an entire DB table). I think we should be able to assume that we can call to_s or inspect to build a log message, for example, and it will not overpower the virtual machine. If a method that returns a large amount of data is needed, it can be implemented with a new name and called explicitly by those who have the specialized knowledge of it.

So I wonder if we're looking in the wrong place for the solution. If ActiveRecord includes reads in an entire table and includes its data in a returned string, IMO this is the problem.

If we assume that implementers of to_s and inspect should limit the size and cost of the returned strings, then it is reasonable to include them in an exception's text as is currently the case.

However, given the severity of the situation, I think Charlie's suggestion is a quicker solution, and one that is more reliable, since we don't know what classes will be coded in the future.

My vote would be to fix this ASAP, even if it means that JRuby deviates slightly from MRI.

@enebo
Copy link
Member

enebo commented May 19, 2016

@keithrbennett are you sure that was the link you intended to include? I do not understand how it is related.

As a general statement I find this to be bugs in various libraries who dump the planet. If you are going to dump a DB as output then perhaps you need to limit output? As a broader statement it also shows how Ruby might have been better off using IO-based API for this (e.g. output(print_io=$stdout)).

I have a different proposal for to_s on NameError. I think arguably this is something where some people will want full output and others will not. We can make a new require:

require 'abbreviated_to_s'

class NameError
   include AbbreviatedToS
end

By including this to NameError we will check to see if it is included and then do a respond_to? :abbreviated_to_s and call that instead.

This will put a possible burden on library author to fix the general issue by making their own abbreviated output. I think though if you plan on not including everything in output it is probably important that library auhors can make a best effort on what is displayed.

A generic implementation can be made as well. It would need to be somewhat reflective and inspect sizes of various ivars and their values and elide values which are too big. So this could be something like:

require 'abbreviated_to_s/reflective'

class ActiveRecord::Base
   include AbbreviatedToS::Reflective
end

Then if people run into this they can add a little magic in a Rails initializer and the problem is fixed.

So on our side we would need NameError (or anything else which would need this) see if the module in question is in its hierarchy and call it instead of to_s if so. Someone would need to write these small shims and I think export them as a gem.

This also feels like it could be baked into JRuby and MRI as well but perhaps just trying to solve this in one impl first might be a good path forward?

Note: abbreviated_to_s and the names I picked are just stakeholder names. I do not really care what name is used so long as people like a name.

@headius
Copy link
Member

headius commented May 20, 2016

@tenderlove @sgrif Might either of you know if this sort of problem has been fixed in ActiveRecord? To be more specific: a NameError raised against a model with a lot of relationships might drag along a LOT of output when the NameError is eventually printed out.

See also https://bugs.ruby-lang.org/issues/9725 which has stalled.

@sgrif
Copy link
Contributor

sgrif commented May 20, 2016

I don't believe so, no. I'm not sure if there's much that we can do on the AR side short of re-implementing NameError

@keithrbennett
Copy link

I may be misunderstanding this issue, but...

@sgrif Is it true that ActiveRecord's to_s is pulling in an entire table's data into the string? If this is so, then I would argue that the issue is not that NameError is calling to_s, but rather that the to_s is building and returning such a huge string to begin with.

I think the higher level question that the community needs to answer is "given the havoc they can wreak, do we want to condone and support huge to_s strings?".

In my opinion, the answer is no, because I think the value of enabling any code to be able to call to_s or inspect on unknown objects without fear of bringing down the system is greater than the value of the freedom to implement huge strings.

@enebo The JRuby discussion was buried in the comments. Search the page for "JRuby" and you'll see it. Also, I'm sure your solution is technically sound, you know way more than I about these things, but I still think that's way more complexity than the freedom to return huge strings is worth.

@sgrif
Copy link
Contributor

sgrif commented May 20, 2016

No we don't pull the entire table. On instances we actually shouldn't be grabbing anything from #inspect. On the class object we load the schema if it's not loaded. On a relation instance we will load the query that it represents.

@headius
Copy link
Member

headius commented May 20, 2016

It seems possible the issues people have had here are not a problem in recent/current Rails...wish we could get some confirmation from someone who has had the issue in the past.

@sgrif
Copy link
Contributor

sgrif commented May 20, 2016

I'd be happy to do more digging if you can point me at more details of what
they were doing that would load so much data

On Fri, May 20, 2016, 9:41 AM Charles Oliver Nutter <
notifications@github.com> wrote:

It seems possible the issues people have had here are not a problem in
recent/current Rails...wish we could get some confirmation from someone who
has had the issue in the past.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#216 (comment)

@mmustala
Copy link

mmustala commented Jun 2, 2016

I tried to create a simple app with the 9k jruby to demonstrate this error but couldn't get it reproduced. My original environment where the error happened is not available any more. I may give it another shot later if I find time for it.

@Hakon
Copy link
Author

Hakon commented Jun 2, 2016

I no longer work on the project where I originally experienced the issue in question. I believe the problem was that activerecord added each field of the record in the to_s.
The table in question had quite a big amount of fields and some of the fields contained large amounts of data.

I hope this helps.

Den 20. mai 2016 kl. 15.41 skrev Charles Oliver Nutter notifications@github.com:

It seems possible the issues people have had here are not a problem in recent/current Rails...wish we could get some confirmation from someone who has had the issue in the past.


You are receiving this because you authored the thread.
Reply to this email directly or view it on GitHub

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

No branches or pull requests

10 participants