United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6980262 Memory leak when exception is thrown in static initializer
JDK-6980262 : Memory leak when exception is thrown in static initializer

Details
Type:
Bug
Submit Date:
2010-08-26
Status:
Closed
Updated Date:
2012-02-01
Project Name:
JDK
Resolved Date:
2011-03-08
Component:
hotspot
OS:
generic
Sub-Component:
runtime
CPU:
generic
Priority:
P2
Resolution:
Fixed
Affected Versions:
hs19
Fixed Versions:
hs20 (b01)

Related Reports
Backport:
Backport:
Backport:
Backport:
Backport:
Backport:

Sub Tasks

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

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.
                                     
2010-08-26
EVALUATION

http://hg.openjdk.java.net/jdk7/hotspot-rt/hotspot/rev/2528b5bd749c
                                     
2010-08-28
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);
                                     
2010-08-30
EVALUATION

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

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

http://hg.openjdk.java.net/hsx/hsx19/baseline/rev/04323991c395
                                     
2010-10-08
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) {}
        }
    }
}
                                     
2010-08-26



Hardware and Software, Engineered to Work Together