8043224: -Xcheck:jni improvements to exception checking and excessive local refs
authordsimms
Wed, 11 Jun 2014 18:34:09 +0200
changeset 25058 4542f853c2ac
parent 25057 f38210f84f8c
child 25059 fff9b51f4be4
8043224: -Xcheck:jni improvements to exception checking and excessive local refs Summary: Warning when not checking exceptions from function that require so, also when local refs expand beyond capacity. Reviewed-by: zgu, coleenp, hseigel
hotspot/src/share/vm/memory/guardedMemory.hpp
hotspot/src/share/vm/prims/jniCheck.cpp
hotspot/src/share/vm/runtime/jniHandles.cpp
hotspot/src/share/vm/runtime/jniHandles.hpp
hotspot/src/share/vm/runtime/thread.cpp
hotspot/src/share/vm/runtime/thread.hpp
--- a/hotspot/src/share/vm/memory/guardedMemory.hpp	Wed Jun 11 09:58:23 2014 -0400
+++ b/hotspot/src/share/vm/memory/guardedMemory.hpp	Wed Jun 11 18:34:09 2014 +0200
@@ -235,7 +235,7 @@
    * @return the size of the user data.
    */
   size_t get_user_size() const {
-    assert(_base_addr, "Not wrapping any memory");
+    assert(_base_addr != NULL, "Not wrapping any memory");
     return get_head_guard()->get_user_size();
   }
 
@@ -245,7 +245,7 @@
    * @return the user data pointer.
    */
   u_char* get_user_ptr() const {
-    assert(_base_addr, "Not wrapping any memory");
+    assert(_base_addr != NULL, "Not wrapping any memory");
     return _base_addr + sizeof(GuardHeader);
   }
 
@@ -281,7 +281,7 @@
     memset(get_user_ptr(), ch, get_user_size());
   }
 
-public:
+ public:
   /**
    * Return the total size required for wrapping the given user size.
    *
--- a/hotspot/src/share/vm/prims/jniCheck.cpp	Wed Jun 11 09:58:23 2014 -0400
+++ b/hotspot/src/share/vm/prims/jniCheck.cpp	Wed Jun 11 18:34:09 2014 +0200
@@ -53,6 +53,8 @@
 # include "jniTypes_ppc.hpp"
 #endif
 
+// Complain every extra number of unplanned local refs
+#define CHECK_JNI_LOCAL_REF_CAP_WARN_THRESHOLD 32
 
 // Heap objects are allowed to be directly referenced only in VM code,
 // not in native code.
@@ -168,12 +170,42 @@
  * SUPPORT FUNCTIONS
  */
 
+/**
+ * Check whether or not a programmer has actually checked for exceptions. According
+ * to the JNI Specification ("jni/spec/design.html#java_exceptions"):
+ *
+ * There are two cases where the programmer needs to check for exceptions without
+ * being able to first check an error code:
+ *
+ * - The JNI functions that invoke a Java method return the result of the Java method.
+ * The programmer must call ExceptionOccurred() to check for possible exceptions
+ * that occurred during the execution of the Java method.
+ *
+ * - Some of the JNI array access functions do not return an error code, but may
+ * throw an ArrayIndexOutOfBoundsException or ArrayStoreException.
+ *
+ * In all other cases, a non-error return value guarantees that no exceptions have been thrown.
+ */
+static inline void
+check_pending_exception(JavaThread* thr) {
+  if (thr->has_pending_exception()) {
+    NativeReportJNIWarning(thr, "JNI call made with exception pending");
+  }
+  if (thr->is_pending_jni_exception_check()) {
+    IN_VM(
+      tty->print_cr("WARNING in native method: JNI call made without checking exceptions when required to from %s",
+        thr->get_pending_jni_exception_check());
+      thr->print_stack();
+    )
+    thr->clear_pending_jni_exception_check(); // Just complain once
+  }
+}
+
+
 static inline void
 functionEnterCritical(JavaThread* thr)
 {
-  if (thr->has_pending_exception()) {
-    NativeReportJNIWarning(thr, "JNI call made with exception pending");
-  }
+  check_pending_exception(thr);
 }
 
 static inline void
@@ -187,9 +219,7 @@
   if (thr->in_critical()) {
     tty->print_cr("%s", warn_other_function_in_critical);
   }
-  if (thr->has_pending_exception()) {
-    NativeReportJNIWarning(thr, "JNI call made with exception pending");
-  }
+  check_pending_exception(thr);
 }
 
 static inline void
@@ -201,9 +231,20 @@
 }
 
 static inline void
-functionExit(JNIEnv *env)
+functionExit(JavaThread* thr)
 {
-  /* nothing to do at this time */
+  JNIHandleBlock* handles = thr->active_handles();
+  size_t planned_capacity = handles->get_planned_capacity();
+  size_t live_handles = handles->get_number_of_live_handles();
+  if (live_handles > planned_capacity) {
+    IN_VM(
+      tty->print_cr("WARNING: JNI local refs: %zu, exceeds capacity: %zu",
+          live_handles, planned_capacity);
+      thr->print_stack();
+    )
+    // Complain just the once, reset to current + warn threshold
+    handles->set_planned_capacity(live_handles + CHECK_JNI_LOCAL_REF_CAP_WARN_THRESHOLD);
+  }
 }
 
 static inline void
@@ -508,7 +549,7 @@
       jniCheck::validate_object(thr, loader);
     )
     jclass result = UNCHECKED()->DefineClass(env, name, loader, buf, len);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -520,7 +561,7 @@
       jniCheck::validate_class_descriptor(thr, name);
     )
     jclass result = UNCHECKED()->FindClass(env, name);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -532,7 +573,7 @@
       jniCheck::validate_object(thr, method);
     )
     jmethodID result = UNCHECKED()->FromReflectedMethod(env, method);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -544,7 +585,7 @@
       jniCheck::validate_object(thr, field);
     )
     jfieldID result = UNCHECKED()->FromReflectedField(env, field);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -560,7 +601,7 @@
     )
     jobject result = UNCHECKED()->ToReflectedMethod(env, cls, methodID,
                                                     isStatic);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -572,7 +613,7 @@
       jniCheck::validate_class(thr, sub, true);
     )
     jclass result = UNCHECKED()->GetSuperclass(env, sub);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -586,7 +627,7 @@
       jniCheck::validate_class(thr, sup, true);
     )
     jboolean result = UNCHECKED()->IsAssignableFrom(env, sub, sup);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -601,7 +642,7 @@
     )
     jobject result = UNCHECKED()->ToReflectedField(env, cls, fieldID,
                                                    isStatic);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -619,7 +660,7 @@
       }
     )
     jint result = UNCHECKED()->Throw(env, obj);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -634,15 +675,16 @@
       jniCheck::validate_throwable_klass(thr, k);
     )
     jint result = UNCHECKED()->ThrowNew(env, clazz, msg);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jthrowable,
   checked_jni_ExceptionOccurred(JNIEnv *env))
+    thr->clear_pending_jni_exception_check();
     functionEnterExceptionAllowed(thr);
     jthrowable result = UNCHECKED()->ExceptionOccurred(env);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -650,22 +692,24 @@
   checked_jni_ExceptionDescribe(JNIEnv *env))
     functionEnterExceptionAllowed(thr);
     UNCHECKED()->ExceptionDescribe(env);
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
   checked_jni_ExceptionClear(JNIEnv *env))
+    thr->clear_pending_jni_exception_check();
     functionEnterExceptionAllowed(thr);
     UNCHECKED()->ExceptionClear(env);
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
   checked_jni_FatalError(JNIEnv *env,
                          const char *msg))
+    thr->clear_pending_jni_exception_check();
     functionEnter(thr);
     UNCHECKED()->FatalError(env, msg);
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(jint,
@@ -675,7 +719,10 @@
     if (capacity < 0)
       NativeReportJNIFatalError(thr, "negative capacity");
     jint result = UNCHECKED()->PushLocalFrame(env, capacity);
-    functionExit(env);
+    if (result == JNI_OK) {
+      thr->active_handles()->set_planned_capacity(capacity + CHECK_JNI_LOCAL_REF_CAP_WARN_THRESHOLD);
+    }
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -684,7 +731,7 @@
                             jobject result))
     functionEnterExceptionAllowed(thr);
     jobject res = UNCHECKED()->PopLocalFrame(env, result);
-    functionExit(env);
+    functionExit(thr);
     return res;
 JNI_END
 
@@ -698,7 +745,7 @@
       }
     )
     jobject result = UNCHECKED()->NewGlobalRef(env,lobj);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -714,7 +761,7 @@
       }
     )
     UNCHECKED()->DeleteGlobalRef(env,gref);
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
@@ -729,7 +776,7 @@
             "Invalid local JNI handle passed to DeleteLocalRef");
     )
     UNCHECKED()->DeleteLocalRef(env, obj);
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(jboolean,
@@ -750,7 +797,7 @@
       }
     )
     jboolean result = UNCHECKED()->IsSameObject(env,obj1,obj2);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -764,7 +811,7 @@
       }
     )
     jobject result = UNCHECKED()->NewLocalRef(env, ref);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -776,7 +823,10 @@
       NativeReportJNIFatalError(thr, "negative capacity");
     }
     jint result = UNCHECKED()->EnsureLocalCapacity(env, capacity);
-    functionExit(env);
+    if (result == JNI_OK) {
+      thr->active_handles()->set_planned_capacity(capacity + CHECK_JNI_LOCAL_REF_CAP_WARN_THRESHOLD);
+    }
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -788,7 +838,7 @@
       jniCheck::validate_class(thr, clazz, false);
     )
     jobject result = UNCHECKED()->AllocObject(env,clazz);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -806,7 +856,7 @@
     va_start(args, methodID);
     jobject result = UNCHECKED()->NewObjectV(env,clazz,methodID,args);
     va_end(args);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -821,7 +871,7 @@
       jniCheck::validate_jmethod_id(thr, methodID);
     )
     jobject result = UNCHECKED()->NewObjectV(env,clazz,methodID,args);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -836,7 +886,7 @@
       jniCheck::validate_jmethod_id(thr, methodID);
     )
     jobject result = UNCHECKED()->NewObjectA(env,clazz,methodID,args);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -848,7 +898,7 @@
       jniCheck::validate_object(thr, obj);
     )
     jclass result = UNCHECKED()->GetObjectClass(env,obj);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -862,7 +912,7 @@
       jniCheck::validate_class(thr, clazz, true);
     )
     jboolean result = UNCHECKED()->IsInstanceOf(env,obj,clazz);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -876,7 +926,7 @@
       jniCheck::validate_class(thr, clazz, false);
     )
     jmethodID result = UNCHECKED()->GetMethodID(env,clazz,name,sig);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -895,7 +945,8 @@
     ResultType result =UNCHECKED()->Call##Result##MethodV(env, obj, methodID, \
                                                           args); \
     va_end(args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("Call"#Result"Method"); \
+    functionExit(thr); \
     return result; \
 JNI_END \
 \
@@ -910,7 +961,8 @@
     ) \
     ResultType result = UNCHECKED()->Call##Result##MethodV(env, obj, methodID,\
                                                            args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("Call"#Result"MethodV"); \
+    functionExit(thr); \
     return result; \
 JNI_END \
 \
@@ -925,7 +977,8 @@
     ) \
     ResultType result = UNCHECKED()->Call##Result##MethodA(env, obj, methodID,\
                                                            args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("Call"#Result"MethodA"); \
+    functionExit(thr); \
     return result; \
 JNI_END
 
@@ -952,7 +1005,8 @@
     va_start(args,methodID);
     UNCHECKED()->CallVoidMethodV(env,obj,methodID,args);
     va_end(args);
-    functionExit(env);
+    thr->set_pending_jni_exception_check("CallVoidMethod");
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
@@ -965,7 +1019,8 @@
       jniCheck::validate_call_object(thr, obj, methodID);
     )
     UNCHECKED()->CallVoidMethodV(env,obj,methodID,args);
-    functionExit(env);
+    thr->set_pending_jni_exception_check("CallVoidMethodV");
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
@@ -978,7 +1033,8 @@
       jniCheck::validate_call_object(thr, obj, methodID);
     )
     UNCHECKED()->CallVoidMethodA(env,obj,methodID,args);
-    functionExit(env);
+    thr->set_pending_jni_exception_check("CallVoidMethodA");
+    functionExit(thr);
 JNI_END
 
 #define WRAPPER_CallNonvirtualMethod(ResultType, Result) \
@@ -1001,7 +1057,8 @@
                                                                      methodID,\
                                                                      args); \
     va_end(args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("CallNonvirtual"#Result"Method"); \
+    functionExit(thr); \
     return result; \
 JNI_END \
 \
@@ -1021,7 +1078,8 @@
                                                                      clazz, \
                                                                      methodID,\
                                                                      args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("CallNonvirtual"#Result"MethodV"); \
+    functionExit(thr); \
     return result; \
 JNI_END \
 \
@@ -1041,7 +1099,8 @@
                                                                      clazz, \
                                                                      methodID,\
                                                                      args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("CallNonvirtual"#Result"MethodA"); \
+    functionExit(thr); \
     return result; \
 JNI_END
 
@@ -1070,7 +1129,8 @@
     va_start(args,methodID);
     UNCHECKED()->CallNonvirtualVoidMethodV(env,obj,clazz,methodID,args);
     va_end(args);
-    functionExit(env);
+    thr->set_pending_jni_exception_check("CallNonvirtualVoidMethod");
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
@@ -1085,7 +1145,8 @@
       jniCheck::validate_call_class(thr, clazz, methodID);
     )
     UNCHECKED()->CallNonvirtualVoidMethodV(env,obj,clazz,methodID,args);
-    functionExit(env);
+    thr->set_pending_jni_exception_check("CallNonvirtualVoidMethodV");
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
@@ -1100,7 +1161,8 @@
       jniCheck::validate_call_class(thr, clazz, methodID);
     )
     UNCHECKED()->CallNonvirtualVoidMethodA(env,obj,clazz,methodID,args);
-    functionExit(env);
+    thr->set_pending_jni_exception_check("CallNonvirtualVoidMethodA");
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(jfieldID,
@@ -1113,7 +1175,7 @@
       jniCheck::validate_class(thr, clazz, false);
     )
     jfieldID result = UNCHECKED()->GetFieldID(env,clazz,name,sig);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1127,7 +1189,7 @@
       checkInstanceFieldID(thr, fieldID, obj, FieldType); \
     ) \
     ReturnType result = UNCHECKED()->Get##Result##Field(env,obj,fieldID); \
-    functionExit(env); \
+    functionExit(thr); \
     return result; \
 JNI_END
 
@@ -1152,7 +1214,7 @@
       checkInstanceFieldID(thr, fieldID, obj, FieldType); \
     ) \
     UNCHECKED()->Set##Result##Field(env,obj,fieldID,val); \
-    functionExit(env); \
+    functionExit(thr); \
 JNI_END
 
 WRAPPER_SetField(jobject,  Object,  T_OBJECT)
@@ -1176,7 +1238,7 @@
       jniCheck::validate_class(thr, clazz, false);
     )
     jmethodID result = UNCHECKED()->GetStaticMethodID(env,clazz,name,sig);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1198,7 +1260,8 @@
                                                                  methodID, \
                                                                  args); \
     va_end(args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("CallStatic"#Result"Method"); \
+    functionExit(thr); \
     return result; \
 JNI_END \
 \
@@ -1216,7 +1279,8 @@
                                                                  clazz, \
                                                                  methodID, \
                                                                  args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("CallStatic"#Result"MethodV"); \
+    functionExit(thr); \
     return result; \
 JNI_END \
 \
@@ -1234,7 +1298,8 @@
                                                                  clazz, \
                                                                  methodID, \
                                                                  args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("CallStatic"#Result"MethodA"); \
+    functionExit(thr); \
     return result; \
 JNI_END
 
@@ -1262,7 +1327,8 @@
     va_start(args,methodID);
     UNCHECKED()->CallStaticVoidMethodV(env,cls,methodID,args);
     va_end(args);
-    functionExit(env);
+    thr->set_pending_jni_exception_check("CallStaticVoidMethod");
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
@@ -1276,7 +1342,8 @@
       jniCheck::validate_class(thr, cls, false);
     )
     UNCHECKED()->CallStaticVoidMethodV(env,cls,methodID,args);
-    functionExit(env);
+    thr->set_pending_jni_exception_check("CallStaticVoidMethodV");
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
@@ -1290,7 +1357,8 @@
       jniCheck::validate_class(thr, cls, false);
     )
     UNCHECKED()->CallStaticVoidMethodA(env,cls,methodID,args);
-    functionExit(env);
+    thr->set_pending_jni_exception_check("CallStaticVoidMethodA");
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(jfieldID,
@@ -1303,7 +1371,7 @@
       jniCheck::validate_class(thr, clazz, false);
     )
     jfieldID result = UNCHECKED()->GetStaticFieldID(env,clazz,name,sig);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1320,7 +1388,7 @@
     ReturnType result = UNCHECKED()->GetStatic##Result##Field(env, \
                                                               clazz, \
                                                               fieldID); \
-    functionExit(env); \
+    functionExit(thr); \
     return result; \
 JNI_END
 
@@ -1346,7 +1414,7 @@
       checkStaticFieldID(thr, fieldID, clazz, FieldType); \
     ) \
     UNCHECKED()->SetStatic##Result##Field(env,clazz,fieldID,value); \
-    functionExit(env); \
+    functionExit(thr); \
 JNI_END
 
 WRAPPER_SetStaticField(jobject,  Object,  T_OBJECT)
@@ -1366,7 +1434,7 @@
                         jsize len))
     functionEnter(thr);
     jstring result = UNCHECKED()->NewString(env,unicode,len);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1378,7 +1446,7 @@
       checkString(thr, str);
     )
     jsize result = UNCHECKED()->GetStringLength(env,str);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1407,7 +1475,7 @@
       // Note that the dtrace arguments for the allocated memory will not match up with this solution.
       FreeHeap((char*)result);
     }
-    functionExit(env);
+    functionExit(thr);
     return new_result;
 JNI_END
 
@@ -1442,7 +1510,7 @@
        UNCHECKED()->ReleaseStringChars(env, str,
            (const jchar*) guarded.release_for_freeing());
     }
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(jstring,
@@ -1450,7 +1518,7 @@
                            const char *utf))
     functionEnter(thr);
     jstring result = UNCHECKED()->NewStringUTF(env,utf);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1462,7 +1530,7 @@
       checkString(thr, str);
     )
     jsize result = UNCHECKED()->GetStringUTFLength(env,str);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1490,7 +1558,7 @@
       // Note that the dtrace arguments for the allocated memory will not match up with this solution.
       FreeHeap((char*)result, mtInternal);
     }
-    functionExit(env);
+    functionExit(thr);
     return new_result;
 JNI_END
 
@@ -1525,7 +1593,7 @@
       UNCHECKED()->ReleaseStringUTFChars(env, str,
           (const char*) guarded.release_for_freeing());
     }
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(jsize,
@@ -1536,7 +1604,7 @@
       check_is_array(thr, array);
     )
     jsize result = UNCHECKED()->GetArrayLength(env,array);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1547,7 +1615,7 @@
                              jobject init))
     functionEnter(thr);
     jobjectArray result = UNCHECKED()->NewObjectArray(env,len,clazz,init);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1560,7 +1628,8 @@
       check_is_obj_array(thr, array);
     )
     jobject result = UNCHECKED()->GetObjectArrayElement(env,array,index);
-    functionExit(env);
+    thr->set_pending_jni_exception_check("GetObjectArrayElement");
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1574,7 +1643,8 @@
       check_is_obj_array(thr, array);
     )
     UNCHECKED()->SetObjectArrayElement(env,array,index,val);
-    functionExit(env);
+    thr->set_pending_jni_exception_check("SetObjectArrayElement");
+    functionExit(thr);
 JNI_END
 
 #define WRAPPER_NewScalarArray(Return, Result) \
@@ -1583,7 +1653,7 @@
                                  jsize len)) \
     functionEnter(thr); \
     Return result = UNCHECKED()->New##Result##Array(env,len); \
-    functionExit(env); \
+    functionExit(thr); \
     return (Return) result; \
 JNI_END
 
@@ -1611,7 +1681,7 @@
     if (result != NULL) { \
       result = (ElementType *) check_jni_wrap_copy_array(thr, array, result); \
     } \
-    functionExit(env); \
+    functionExit(thr); \
     return result; \
 JNI_END
 
@@ -1639,7 +1709,7 @@
     ElementType* orig_result = (ElementType *) check_wrapped_array_release( \
         thr, "checked_jni_Release"#Result"ArrayElements", array, elems, mode); \
     UNCHECKED()->Release##Result##ArrayElements(env, array, orig_result, mode); \
-    functionExit(env); \
+    functionExit(thr); \
 JNI_END
 
 WRAPPER_ReleaseScalarArrayElements(T_BOOLEAN,jboolean, Boolean, bool)
@@ -1663,7 +1733,8 @@
       check_primitive_array_type(thr, array, ElementTag); \
     ) \
     UNCHECKED()->Get##Result##ArrayRegion(env,array,start,len,buf); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("Get"#Result"ArrayRegion"); \
+    functionExit(thr); \
 JNI_END
 
 WRAPPER_GetScalarArrayRegion(T_BOOLEAN, jboolean, Boolean)
@@ -1687,7 +1758,8 @@
       check_primitive_array_type(thr, array, ElementTag); \
     ) \
     UNCHECKED()->Set##Result##ArrayRegion(env,array,start,len,buf); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("Set"#Result"ArrayRegion"); \
+    functionExit(thr); \
 JNI_END
 
 WRAPPER_SetScalarArrayRegion(T_BOOLEAN, jboolean, Boolean)
@@ -1706,7 +1778,7 @@
                               jint nMethods))
     functionEnter(thr);
     jint result = UNCHECKED()->RegisterNatives(env,clazz,methods,nMethods);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1715,7 +1787,7 @@
                                 jclass clazz))
     functionEnter(thr);
     jint result = UNCHECKED()->UnregisterNatives(env,clazz);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1727,7 +1799,7 @@
       jniCheck::validate_object(thr, obj);
     )
     jint result = UNCHECKED()->MonitorEnter(env,obj);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1739,7 +1811,7 @@
       jniCheck::validate_object(thr, obj);
     )
     jint result = UNCHECKED()->MonitorExit(env,obj);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1748,7 +1820,7 @@
                         JavaVM **vm))
     functionEnter(thr);
     jint result = UNCHECKED()->GetJavaVM(env,vm);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1763,7 +1835,8 @@
       checkString(thr, str);
     )
     UNCHECKED()->GetStringRegion(env, str, start, len, buf);
-    functionExit(env);
+    thr->set_pending_jni_exception_check("GetStringRegion");
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
@@ -1777,7 +1850,8 @@
       checkString(thr, str);
     )
     UNCHECKED()->GetStringUTFRegion(env, str, start, len, buf);
-    functionExit(env);
+    thr->set_pending_jni_exception_check("GetStringUTFRegion");
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(void *,
@@ -1792,7 +1866,7 @@
     if (result != NULL) {
       result = check_jni_wrap_copy_array(thr, array, result);
     }
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1808,7 +1882,7 @@
     // Check the element array...
     void* orig_result = check_wrapped_array_release(thr, "ReleasePrimitiveArrayCritical", array, carray, mode);
     UNCHECKED()->ReleasePrimitiveArrayCritical(env, array, orig_result, mode);
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(const jchar*,
@@ -1820,7 +1894,7 @@
       checkString(thr, string);
     )
     const jchar *result = UNCHECKED()->GetStringCritical(env, string, isCopy);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1836,7 +1910,7 @@
      * string parameter as a minor sanity check
      */
     UNCHECKED()->ReleaseStringCritical(env, str, chars);
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(jweak,
@@ -1849,7 +1923,7 @@
       }
     )
     jweak result = UNCHECKED()->NewWeakGlobalRef(env, obj);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1858,14 +1932,15 @@
                                   jweak ref))
     functionEnterExceptionAllowed(thr);
     UNCHECKED()->DeleteWeakGlobalRef(env, ref);
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(jboolean,
   checked_jni_ExceptionCheck(JNIEnv *env))
+    thr->clear_pending_jni_exception_check();
     functionEnterExceptionAllowed(thr);
     jboolean result = UNCHECKED()->ExceptionCheck(env);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1875,7 +1950,7 @@
                                   jlong capacity))
     functionEnter(thr);
     jobject result = UNCHECKED()->NewDirectByteBuffer(env, address, capacity);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1884,7 +1959,7 @@
                                      jobject buf))
     functionEnter(thr);
     void* result = UNCHECKED()->GetDirectBufferAddress(env, buf);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1893,7 +1968,7 @@
                                       jobject buf))
     functionEnter(thr);
     jlong result = UNCHECKED()->GetDirectBufferCapacity(env, buf);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1906,7 +1981,7 @@
       jniCheck::validate_object(thr, obj);
     )
     jobjectRefType result = UNCHECKED()->GetObjectRefType(env, obj);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
@@ -1915,7 +1990,7 @@
   checked_jni_GetVersion(JNIEnv *env))
     functionEnter(thr);
     jint result = UNCHECKED()->GetVersion(env);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
--- a/hotspot/src/share/vm/runtime/jniHandles.cpp	Wed Jun 11 09:58:23 2014 -0400
+++ b/hotspot/src/share/vm/runtime/jniHandles.cpp	Wed Jun 11 18:34:09 2014 +0200
@@ -298,6 +298,7 @@
   block->_top  = 0;
   block->_next = NULL;
   block->_pop_frame_link = NULL;
+  block->_planned_capacity = block_size_in_oops;
   // _last, _free_list & _allocate_before_rebuild initialized in allocate_handle
   debug_only(block->_last = NULL);
   debug_only(block->_free_list = NULL);
@@ -531,6 +532,12 @@
   return result;
 }
 
+const size_t JNIHandleBlock::get_number_of_live_handles() {
+  CountHandleClosure counter;
+  oops_do(&counter);
+  return counter.count();
+}
+
 // This method is not thread-safe, i.e., must be called while holding a lock on the
 // structure.
 long JNIHandleBlock::memory_usage() const {
--- a/hotspot/src/share/vm/runtime/jniHandles.hpp	Wed Jun 11 09:58:23 2014 -0400
+++ b/hotspot/src/share/vm/runtime/jniHandles.hpp	Wed Jun 11 18:34:09 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -112,6 +112,9 @@
   oop*            _free_list;                   // Handle free list
   int             _allocate_before_rebuild;     // Number of blocks to allocate before rebuilding free list
 
+  // Check JNI, "planned capacity" for current frame (or push/ensure)
+  size_t          _planned_capacity;
+
   #ifndef PRODUCT
   JNIHandleBlock* _block_list_link;             // Link for list below
   static JNIHandleBlock* _block_list;           // List of all allocated blocks (for debugging only)
@@ -152,6 +155,11 @@
   // Traversal of weak handles. Unreachable oops are cleared.
   void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f);
 
+  // Checked JNI support
+  void set_planned_capacity(size_t planned_capacity) { _planned_capacity = planned_capacity; }
+  const size_t get_planned_capacity() { return _planned_capacity; }
+  const size_t get_number_of_live_handles();
+
   // Debugging
   bool chain_contains(jobject handle) const;    // Does this block or following blocks contain handle
   bool contains(jobject handle) const;          // Does this block contain handle
--- a/hotspot/src/share/vm/runtime/thread.cpp	Wed Jun 11 09:58:23 2014 -0400
+++ b/hotspot/src/share/vm/runtime/thread.cpp	Wed Jun 11 18:34:09 2014 +0200
@@ -1448,6 +1448,7 @@
   _thread_stat = new ThreadStatistics();
   _blocked_on_compilation = false;
   _jni_active_critical = 0;
+  _pending_jni_exception_check_fn = NULL;
   _do_not_unlock_if_synchronized = false;
   _cached_monitor_info = NULL;
   _parker = Parker::Allocate(this) ;
--- a/hotspot/src/share/vm/runtime/thread.hpp	Wed Jun 11 09:58:23 2014 -0400
+++ b/hotspot/src/share/vm/runtime/thread.hpp	Wed Jun 11 18:34:09 2014 +0200
@@ -915,6 +915,9 @@
   // support for JNI critical regions
   jint    _jni_active_critical;                  // count of entries into JNI critical region
 
+  // Checked JNI: function name requires exception check
+  char* _pending_jni_exception_check_fn;
+
   // For deadlock detection.
   int _depth_first_number;
 
@@ -1400,6 +1403,12 @@
                           assert(_jni_active_critical >= 0,
                                  "JNI critical nesting problem?"); }
 
+  // Checked JNI, is the programmer required to check for exceptions, specify which function name
+  bool is_pending_jni_exception_check() const { return _pending_jni_exception_check_fn != NULL; }
+  void clear_pending_jni_exception_check() { _pending_jni_exception_check_fn = NULL; }
+  const char* get_pending_jni_exception_check() const { return _pending_jni_exception_check_fn; }
+  void set_pending_jni_exception_check(const char* fn_name) { _pending_jni_exception_check_fn = (char*) fn_name; }
+
   // For deadlock detection
   int depth_first_number() { return _depth_first_number; }
   void set_depth_first_number(int dfn) { _depth_first_number = dfn; }