JDK-6980262 : Memory leak when exception is thrown in static initializer
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: hs19
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2010-08-26
  • Updated: 2012-02-01
  • Resolved: 2011-03-08
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 6 JDK 7 Other
6u21pFixed 7Fixed hs19Fixed
Description
The attached test case tries to load and initialize the following class in a loop:

/** Class that will throw exception in clinit. */
final class ErrorInClinit {
    static {
        willThrowError();
    }
    /** Implemented default constructor. */
    private ErrorInClinit() { }
    /** Method that throws error. */
    static void willThrowError() {
        throw new NoClassDefFoundError("check");
    }
}

Process size grows rapidly, and eventually hits OS limits:

$ ulimit -v 2000000

$ /set/vmsqe/jdk/re/7/promoted/ea/b106/binaries/linux-amd64/bin/java KS &

$ jps -l | grep KS | (read a b; while :; do ps -o pid,size,rss,vsize --no-headers $a; sleep 10; done)
14121 1308576 56720 1356100
14121 1439648 171100 1487172
14121 1505184 285096 1552708
14121 1636256 399216 1683780
14121 1767328 513372 1814852
14121 1898400 627564 1945924
#
# A fatal error has been detected by the Java Runtime Environment:
#
# java.lang.OutOfMemoryError: requested 41 bytes for char in /BUILD_AREA/jdk7/hotspot/src/share/vm/oops/instanceKlass.cpp. Out of swap space?
#
#  Internal Error (allocation.inline.hpp:39), pid=14121, tid=139729910605584
#  Error: char in /BUILD_AREA/jdk7/hotspot/src/share/vm/oops/instanceKlass.cpp
#
# JRE version: 7.0
# Java VM: Java HotSpot(TM) 64-Bit Server VM (19.0-b05 mixed mode linux-amd64 compressed oops)
# An error report file with more information is saved as:
# /set/vmsqe/home/sd208054/ws/ksleak/src/hs_err_pid14121.log
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp

Comments
EVALUATION http://hg.openjdk.java.net/hsx/hsx19/baseline/rev/04323991c395
08-10-2010

EVALUATION http://hg.openjdk.java.net/jdk7/hotspot-gc/hotspot/rev/2528b5bd749c
04-09-2010

EVALUATION http://hg.openjdk.java.net/jdk7/hotspot/hotspot/rev/2528b5bd749c
30-08-2010

EVALUATION diff --git a/src/share/vm/oops/instanceKlass.cpp b/src/share/vm/oops/instanceKlass.cpp --- a/src/share/vm/oops/instanceKlass.cpp +++ b/src/share/vm/oops/instanceKlass.cpp @@ -382,7 +382,7 @@ void instanceKlass::initialize_impl(inst const char* desc = "Could not initialize class "; const char* className = this_oop->external_name(); size_t msglen = strlen(desc) + strlen(className) + 1; - char* message = NEW_C_HEAP_ARRAY(char, msglen); + char* message = NEW_RESOURCE_ARRAY(char, msglen); if (NULL == message) { // Out of memory: can't create detailed error message THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className);
30-08-2010

EVALUATION http://hg.openjdk.java.net/jdk7/hotspot-rt/hotspot/rev/2528b5bd749c
28-08-2010

EVALUATION We're leaking the message string from here: // Step 5 if (this_oop->is_in_error_state()) { DTRACE_CLASSINIT_PROBE_WAIT(erroneous, instanceKlass::cast(this_oop()), -1,wait); ResourceMark rm(THREAD); const char* desc = "Could not initialize class "; const char* className = this_oop->external_name(); size_t msglen = strlen(desc) + strlen(className) + 1; char* message = NEW_C_HEAP_ARRAY(char, msglen); if (NULL == message) { // Out of memory: can't create detailed error message THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className); } else { jio_snprintf(message, msglen, "%s%s", desc, className); THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message); } } The char* contents will get copied into a Java String's internal char[] when creating the Exception object, and we never free the original array.
26-08-2010

EVALUATION I tried valgrid to spot the problem and it showed very suspicious alloction site: malloc os::malloc(unsigned long) instanceKlass::initialize_impl(instanceKlassHandle, Thread*) instanceKlass::initialize(Thread*) (in /opt/sun-jdk-1.6.0.17/jre/lib/amd64/server/libjvm.so) find_class_from_class_loader(JNIEnv_*, symbolHandle, unsigned char, Handle, Handle, unsigned char, Thread*) jvm_find_class_from_class_loader(JNIEnv_*, char const*, unsigned char, _jobject*, unsigned char, Thread*) JVM_FindClassFromClassLoader Java_java_lang_Class_forName0 349,860 bytes in 9,996 blocks are definitely lost in loss record 1,162 of 1,163 I used the test case listed below. It receives an integer as an input - a number of loading attempts. Reported amount of leaked memory in aforementioned allocation site depends on the number of attempts. Test.java: class Invalid { static { if (5>0) throw new RuntimeException(); // to deceive javac } } public class Test { public static void main (String[] args) throws Exception { int count = Integer.parseInt(args[0]); for (int i=0; i < count; i++) { try { Class.forName("Invalid"); } catch (Throwable t) {} } } }
26-08-2010