JDK-6294934 : Memory allocation produce unhandled OOM
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 6
  • Priority: P3
  • Status: Closed
  • Resolution: Not an Issue
  • OS: generic
  • CPU: x86
  • Submitted: 2005-07-08
  • Updated: 2012-02-01
  • Resolved: 2006-03-10
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7Resolved
Related Reports
Relates :  
Relates :  
Description
runtime options:
java -XX:+<gctype> -Xmx768m gctest.Main

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at gctest.ListProducer.Fill(Main.java:40)
        at gctest.Main.main(Main.java:9)

So despite handler defined for Main.java:40 exception terminates VM as unhandled one.
PS It may be hard to reproduce, i can't say exactly, but removing comments from additional output remove this problem in my case.
###@###.### 2005-07-08 11:46:42 GMT

Comments
EVALUATION This bug seems to be really not exactly a bug. Here's DBX stacktrace captured when exception is thrown: =>[1] report_java_out_of_memory(message = 0xbf50b010 "Java heap space") (optimized), at 0xbee8f268 (line ~286) in "debug.cpp" [2] CollectedHeap::common_mem_allocate_noinit(size = ???, is_noref = ???, __the_thread__ = ???) (optimized), at 0xbec5bdad (line ~99) in "collectedHeap.inline.hpp" [3] CollectedHeap::obj_allocate(klass = CLASS, size = ???, __the_thread__ = ???) (optimized), at 0xbef18e6d (line ~163) in "collectedHeap.inline.hpp" [4] instanceKlass::allocate_instance(this = ???, __the_thread__ = ???) (optimized), at 0xbef046e6 (line ~491) in "instanceKlass.cpp" [5] java_lang_String::basic_create(length = ???, tenured = ???, __the_thread__ = ???) (optimized), at 0xbef6c143 (line ~40) in "javaClasses.cpp" [6] java_lang_String::create_from_symbol(symbol = CLASS, __the_thread__ = ???) (optimized), at 0xbef6ce5c (line ~105) in "javaClasses.cpp" [7] SystemDictionary::load_instance_class(class_name = CLASS, class_loader = CLASS, __the_thread__ = ???) (optimized), at 0xbf3def0c (line ~1176) in "systemDictionary.cpp" [8] SystemDictionary::resolve_instance_class_or_null(class_name = CLASS, class_loader = CLASS, protection_domain = CLASS, __the_thread__ = ???) (optimized), at 0xbf3da399 (line ~733) in "systemDictionary.cpp" [9] SystemDictionary::resolve_or_null(class_name = CLASS, class_loader = CLASS, protection_domain = CLASS, __the_thread__ = ???) (optimized), at 0xbf3d62fd (line ~206) in "systemDictionary.cpp" [10] SystemDictionary::resolve_or_fail(class_name = CLASS, class_loader = CLASS, protection_domain = CLASS, throw_error = ???, __the_thread__ = ???) (optimized), at 0xbf3d584e (line ~155) in "systemDictionary.cpp" [11] constantPoolOopDesc::klass_at_impl(this_oop = CLASS, which = ???, __the_thread__ = ???) (optimized), at 0xbee7d3d9 (line ~61) in "constantPoolOop.cpp" [12] constantPoolOopDesc::klass_at(this = ???, which = ???, __the_thread__ = ???) (optimized), at 0xbed5f1b5 (line ~206) in "constantPoolOop.hpp" [13] methodOopDesc::fast_exception_handler_bci_for(this = ???, ex_klass = CLASS, throw_bci = ???, __the_thread__ = ???) (optimized), at 0xbf2b50cf (line ~101) in "methodOop.cpp" [14] SharedRuntime::compute_compiled_exc_handler(nm = ???, ret_pc = ???, exception = CLASS, force_unwind = ???, top_frame_only = ???) (optimized), at 0xbf385bbf (line ~440) in "sharedRuntime.cpp" [15] exception_handler_for_pc_helper(thread = ???, ex = ???, pc = ???, nm = ???) (optimized), at 0xbed56376 (line ~432) in "c1_Runtime1.cpp" [16] Runtime1::exception_handler_for_pc(thread = ???) (optimized), at 0xbed56536 (line ~472) in "c1_Runtime1.cpp" What we see here is that during exception catching we're trying to use exception handling table of method gctest.ListProducer.Fill. It has one single record: Exception table: from to target type 2 48 51 Class java/lang/Throwable So we're trying to resolve exception class, this line in SystemDictionary::load_instance_class Handle s = java_lang_String::create_from_symbol(class_name, CHECK_(nh)); leads to yet another OOME. Handler not yet started execution, so no space can be reclaimed yet. Then SharedRuntime::compute_compiled_exc_handler() which have code to handle exactly that case catches exception, and effectively rethrows it from bci == start of exception handler. That's exactly what's described in the bug. Adding Throwable t = new Throwable() before doing anything else allows testcase to pass. So I'd suggest close it as not a bug, as it seems to be pretty hard to implement SharedRuntime::compute_compiled_exc_handler() with no allocations in Java heap at all.
10-03-2006

EVALUATION See the comment section. A OOM is thrown and catch as expected. A second OOM exception is thrown is the "catch (Throwable e)" and propagates up and causes the termination of the test. I'll be closing this as not a bug.
20-12-2005

EVALUATION Out-of-memory exceptions can be thrown repeatedly in consecutive collections if the gc time limit is not being met. The policy should be changed so that consecutive out-of-memory exceptions should not be thrown but rather that some number of collections should be allowed before the gc time limit can cause an out-of-memory to be thrown again.
28-11-2005

WORK AROUND -XX:-UseGCTimeLimit ###@###.### 2005-07-11 22:41:25 GMT
11-07-2005

EVALUATION I've been able to reproduce this problem with the -XX:+UseParallelGC collector, but not with the -XX:+UseSerialGC collector. Here's what I think is happening. Because your overheads are larger than you thought they were (see 6294930), you are running out of memory, and spending all your collecting for allocation. The -XX:+UseParallelGC collector will throw OutOfMemoryError at you if it spends too much time collecting. Thatcauses us to throw OutOfMemoryError at you, as you prepared for. So we get into the catch block at gctest/Main.java:44. There you null out the variables root and cur, which essentially releases all the space. But that doesn't cause the space to be available, until the next collection. Then you call e.printStackTrace(), which needs to allocate some memory. But even though you've dropped your last reference to the list, we still haven't collected the storage yet, so we go to start a collection, but find we've been spending too much time collecting, so we throw an OutOfMemoryError at you. Now, instead of being the range of the try block, we are in the range of the catch block, which doesn't have a try block in it, so the exception propagates out to the main method and terminates the program. There are some bugs in this theory (or, bugs in our implementation, or both). If you change the catch block to do nothing that might cause allocation, e.g., } catch (Throwable e) { // Don't do anything that would provoke a GC in here! root = null; cur = null; } and then afterwards call System.gc(), you seem to get the same result. I think that's because we have a single, pre-allocated OutOfMemoryError instance that we throw, and I think once it's filled in we don't rewrite it. That would be a bug. I don't know how hard it would be to rewrite the exception if we needed to reuse it. I think another problem is that I think we throw OutOfMemoryError for spending too much time collecting *even if* the collections are because the user is calling System.gc(). It seems like if the user calls System.gc() we should collect, even if we are running over our time limit. One can demonstrate that by adding the following code right after the (minimal) catch block: try { Thread.sleep(1000); } catch (InterruptedException ie) { // Nothing } // Force a GC, to get back space, and // to allow allocation without causing collection. System.gc(); where the Thread.sleep(1000) is enough time to convince the collector that it isn't spending too much time collecting. I don't know how hard it would be to know that a collection was caused by a call to System.gc() and ignore the timing constraint. ###@###.### 2005-07-11 22:14:17 GMT My results are quite differ from evaluation and workaround submitted. 1) -XX:+UseSerialGC Failure occures, -XX:-UseGCTimeLimit won't help to remove it. 2) -XX:+UseParallelGC Failure occures, -XX:-UseGCTimeLimit won't help to remove it. 3) -XX:+UseConcMarkSweepGC No failures, but strange messages with +XX:+PrintGCDetails: GC time would exceed GCTimeLimit of 98% PS I think such behavior is a defect, because application can't restore itself after OOM occured. This is exactly like fatal crash. Even if -XX:-UseGCTimeLimit and System.gc() will help after the fix delivired i think it is still will be the bad case. Better solution maybe is to automaticly ignore GC time statistic after initial OOM being handled by user code. PPS Steps to reproduce: ssh roborooter.sfbay cd /tmp/b6294934 /java/re/jdk/6.0/promoted/ea/b42/binaries/linux-i586/bin/java -Xmx128m -XX:+UseSerialGC -XX:+PrintGCDetails -XX:-UseGCTimeLimit gctest.Main ###@###.### 2005-07-12 08:38:34 GMT
11-07-2005