src/share/vm/prims/jniCheck.cpp

Print this page
rev 6562 : 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

@@ -51,10 +51,12 @@
 #endif
 #ifdef TARGET_ARCH_ppc
 # 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.
 
 #define ASSERT_OOPS_ALLOWED                                          \

@@ -166,16 +168,46 @@
 
 /*
  * 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
-functionEnterCritical(JavaThread* thr)
-{
+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)
+{
+  check_pending_exception(thr);
 }
 
 static inline void
 functionEnterCriticalExceptionAllowed(JavaThread* thr)
 {

@@ -185,13 +217,11 @@
 functionEnter(JavaThread* thr)
 {
   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
 functionEnterExceptionAllowed(JavaThread* thr)
 {

@@ -199,13 +229,24 @@
     tty->print_cr("%s", warn_other_function_in_critical);
   }
 }
 
 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
 checkStaticFieldID(JavaThread* thr, jfieldID fid, jclass cls, int ftype)
 {

@@ -506,11 +547,11 @@
     functionEnter(thr);
     IN_VM(
       jniCheck::validate_object(thr, loader);
     )
     jclass result = UNCHECKED()->DefineClass(env, name, loader, buf, len);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jclass,
   checked_jni_FindClass(JNIEnv *env,

@@ -518,11 +559,11 @@
     functionEnter(thr);
     IN_VM(
       jniCheck::validate_class_descriptor(thr, name);
     )
     jclass result = UNCHECKED()->FindClass(env, name);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jmethodID,
   checked_jni_FromReflectedMethod(JNIEnv *env,

@@ -530,11 +571,11 @@
     functionEnter(thr);
     IN_VM(
       jniCheck::validate_object(thr, method);
     )
     jmethodID result = UNCHECKED()->FromReflectedMethod(env, method);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jfieldID,
   checked_jni_FromReflectedField(JNIEnv *env,

@@ -542,11 +583,11 @@
     functionEnter(thr);
     IN_VM(
       jniCheck::validate_object(thr, field);
     )
     jfieldID result = UNCHECKED()->FromReflectedField(env, field);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jobject,
   checked_jni_ToReflectedMethod(JNIEnv *env,

@@ -558,11 +599,11 @@
       jniCheck::validate_class(thr, cls, false);
       jniCheck::validate_jmethod_id(thr, methodID);
     )
     jobject result = UNCHECKED()->ToReflectedMethod(env, cls, methodID,
                                                     isStatic);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jclass,
   checked_jni_GetSuperclass(JNIEnv *env,

@@ -570,11 +611,11 @@
     functionEnter(thr);
     IN_VM(
       jniCheck::validate_class(thr, sub, true);
     )
     jclass result = UNCHECKED()->GetSuperclass(env, sub);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jboolean,
   checked_jni_IsAssignableFrom(JNIEnv *env,

@@ -584,11 +625,11 @@
     IN_VM(
       jniCheck::validate_class(thr, sub, true);
       jniCheck::validate_class(thr, sup, true);
     )
     jboolean result = UNCHECKED()->IsAssignableFrom(env, sub, sup);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jobject,
   checked_jni_ToReflectedField(JNIEnv *env,

@@ -599,11 +640,11 @@
     IN_VM(
       jniCheck::validate_class(thr, cls, false);
     )
     jobject result = UNCHECKED()->ToReflectedField(env, cls, fieldID,
                                                    isStatic);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jint,
   checked_jni_Throw(JNIEnv *env,

@@ -617,11 +658,11 @@
       } else {
         jniCheck::validate_throwable_klass(thr, oopObj->klass());
       }
     )
     jint result = UNCHECKED()->Throw(env, obj);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jint,
   checked_jni_ThrowNew(JNIEnv *env,

@@ -632,61 +673,67 @@
       Klass* k = jniCheck::validate_class(thr, clazz, false);
       assert(k != NULL, "validate_class shouldn't return NULL Klass*");
       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
 
 JNI_ENTRY_CHECKED(void,
   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,
   checked_jni_PushLocalFrame(JNIEnv *env,
                              jint capacity))
     functionEnterExceptionAllowed(thr);
     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
 
 JNI_ENTRY_CHECKED(jobject,
   checked_jni_PopLocalFrame(JNIEnv *env,
                             jobject result))
     functionEnterExceptionAllowed(thr);
     jobject res = UNCHECKED()->PopLocalFrame(env, result);
-    functionExit(env);
+    functionExit(thr);
     return res;
 JNI_END
 
 JNI_ENTRY_CHECKED(jobject,
   checked_jni_NewGlobalRef(JNIEnv *env,

@@ -696,11 +743,11 @@
       if (lobj != NULL) {
         jniCheck::validate_handle(thr, lobj);
       }
     )
     jobject result = UNCHECKED()->NewGlobalRef(env,lobj);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
   checked_jni_DeleteGlobalRef(JNIEnv *env,

@@ -712,11 +759,11 @@
         ReportJNIFatalError(thr,
             "Invalid global JNI handle passed to DeleteGlobalRef");
       }
     )
     UNCHECKED()->DeleteGlobalRef(env,gref);
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
   checked_jni_DeleteLocalRef(JNIEnv *env,
                              jobject obj))

@@ -727,11 +774,11 @@
                    JNIHandles::is_frame_handle(thr, obj)))
         ReportJNIFatalError(thr,
             "Invalid local JNI handle passed to DeleteLocalRef");
     )
     UNCHECKED()->DeleteLocalRef(env, obj);
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(jboolean,
   checked_jni_IsSameObject(JNIEnv *env,
                            jobject obj1,

@@ -748,11 +795,11 @@
       if (obj2 != NULL && jniCheck::validate_handle(thr, obj2) != NULL) {
         jniCheck::validate_object(thr, obj2);
       }
     )
     jboolean result = UNCHECKED()->IsSameObject(env,obj1,obj2);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jobject,
   checked_jni_NewLocalRef(JNIEnv *env,

@@ -762,11 +809,11 @@
       if (ref != NULL) {
         jniCheck::validate_handle(thr, ref);
       }
     )
     jobject result = UNCHECKED()->NewLocalRef(env, ref);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jint,
   checked_jni_EnsureLocalCapacity(JNIEnv *env,

@@ -774,11 +821,14 @@
     functionEnter(thr);
     if (capacity < 0) {
       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
 
 JNI_ENTRY_CHECKED(jobject,
   checked_jni_AllocObject(JNIEnv *env,

@@ -786,11 +836,11 @@
     functionEnter(thr);
     IN_VM(
       jniCheck::validate_class(thr, clazz, false);
     )
     jobject result = UNCHECKED()->AllocObject(env,clazz);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jobject,
   checked_jni_NewObject(JNIEnv *env,

@@ -804,11 +854,11 @@
       jniCheck::validate_jmethod_id(thr, methodID);
     )
     va_start(args, methodID);
     jobject result = UNCHECKED()->NewObjectV(env,clazz,methodID,args);
     va_end(args);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jobject,
   checked_jni_NewObjectV(JNIEnv *env,

@@ -819,11 +869,11 @@
     IN_VM(
       jniCheck::validate_class(thr, clazz, false);
       jniCheck::validate_jmethod_id(thr, methodID);
     )
     jobject result = UNCHECKED()->NewObjectV(env,clazz,methodID,args);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jobject,
   checked_jni_NewObjectA(JNIEnv *env,

@@ -834,11 +884,11 @@
     IN_VM(
       jniCheck::validate_class(thr, clazz, false);
       jniCheck::validate_jmethod_id(thr, methodID);
     )
     jobject result = UNCHECKED()->NewObjectA(env,clazz,methodID,args);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jclass,
   checked_jni_GetObjectClass(JNIEnv *env,

@@ -846,11 +896,11 @@
     functionEnter(thr);
     IN_VM(
       jniCheck::validate_object(thr, obj);
     )
     jclass result = UNCHECKED()->GetObjectClass(env,obj);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jboolean,
   checked_jni_IsInstanceOf(JNIEnv *env,

@@ -860,11 +910,11 @@
     IN_VM(
       jniCheck::validate_object(thr, obj);
       jniCheck::validate_class(thr, clazz, true);
     )
     jboolean result = UNCHECKED()->IsInstanceOf(env,obj,clazz);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jmethodID,
   checked_jni_GetMethodID(JNIEnv *env,

@@ -874,11 +924,11 @@
     functionEnter(thr);
     IN_VM(
       jniCheck::validate_class(thr, clazz, false);
     )
     jmethodID result = UNCHECKED()->GetMethodID(env,clazz,name,sig);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 #define WRAPPER_CallMethod(ResultType, Result) \
 JNI_ENTRY_CHECKED(ResultType,  \

@@ -893,11 +943,12 @@
     ) \
     va_start(args,methodID); \
     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 \
 \
 JNI_ENTRY_CHECKED(ResultType,  \
   checked_jni_Call##Result##MethodV(JNIEnv *env, \

@@ -908,11 +959,12 @@
     IN_VM(\
       jniCheck::validate_call_object(thr, obj, methodID); \
     ) \
     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 \
 \
 JNI_ENTRY_CHECKED(ResultType,  \
   checked_jni_Call##Result##MethodA(JNIEnv *env, \

@@ -923,11 +975,12 @@
     IN_VM( \
       jniCheck::validate_call_object(thr, obj, methodID); \
     ) \
     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
 
 WRAPPER_CallMethod(jobject,Object)
 WRAPPER_CallMethod(jboolean,Boolean)

@@ -950,11 +1003,12 @@
       jniCheck::validate_call_object(thr, obj, methodID);
     )
     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,
   checked_jni_CallVoidMethodV(JNIEnv *env,
                               jobject obj,

@@ -963,11 +1017,12 @@
     functionEnter(thr);
     IN_VM(
       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,
   checked_jni_CallVoidMethodA(JNIEnv *env,
                               jobject obj,

@@ -976,11 +1031,12 @@
     functionEnter(thr);
     IN_VM(
       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) \
 JNI_ENTRY_CHECKED(ResultType,  \
   checked_jni_CallNonvirtual##Result##Method(JNIEnv *env, \

@@ -999,11 +1055,12 @@
                                                                      obj, \
                                                                      clazz, \
                                                                      methodID,\
                                                                      args); \
     va_end(args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("CallNonvirtual"#Result"Method"); \
+    functionExit(thr); \
     return result; \
 JNI_END \
 \
 JNI_ENTRY_CHECKED(ResultType,  \
   checked_jni_CallNonvirtual##Result##MethodV(JNIEnv *env, \

@@ -1019,11 +1076,12 @@
     ResultType result = UNCHECKED()->CallNonvirtual##Result##MethodV(env, \
                                                                      obj, \
                                                                      clazz, \
                                                                      methodID,\
                                                                      args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("CallNonvirtual"#Result"MethodV"); \
+    functionExit(thr); \
     return result; \
 JNI_END \
 \
 JNI_ENTRY_CHECKED(ResultType,  \
   checked_jni_CallNonvirtual##Result##MethodA(JNIEnv *env, \

@@ -1039,11 +1097,12 @@
     ResultType result = UNCHECKED()->CallNonvirtual##Result##MethodA(env, \
                                                                      obj, \
                                                                      clazz, \
                                                                      methodID,\
                                                                      args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("CallNonvirtual"#Result"MethodA"); \
+    functionExit(thr); \
     return result; \
 JNI_END
 
 WRAPPER_CallNonvirtualMethod(jobject,Object)
 WRAPPER_CallNonvirtualMethod(jboolean,Boolean)

@@ -1068,11 +1127,12 @@
       jniCheck::validate_call_class(thr, clazz, methodID);
     )
     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,
   checked_jni_CallNonvirtualVoidMethodV(JNIEnv *env,
                                         jobject obj,

@@ -1083,11 +1143,12 @@
     IN_VM(
       jniCheck::validate_call_object(thr, obj, methodID);
       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,
   checked_jni_CallNonvirtualVoidMethodA(JNIEnv *env,
                                         jobject obj,

@@ -1098,11 +1159,12 @@
     IN_VM(
       jniCheck::validate_call_object(thr, obj, methodID);
       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,
   checked_jni_GetFieldID(JNIEnv *env,
                          jclass clazz,

@@ -1111,11 +1173,11 @@
     functionEnter(thr);
     IN_VM(
       jniCheck::validate_class(thr, clazz, false);
     )
     jfieldID result = UNCHECKED()->GetFieldID(env,clazz,name,sig);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 #define WRAPPER_GetField(ReturnType,Result,FieldType) \
 JNI_ENTRY_CHECKED(ReturnType,  \

@@ -1125,11 +1187,11 @@
     functionEnter(thr); \
     IN_VM( \
       checkInstanceFieldID(thr, fieldID, obj, FieldType); \
     ) \
     ReturnType result = UNCHECKED()->Get##Result##Field(env,obj,fieldID); \
-    functionExit(env); \
+    functionExit(thr); \
     return result; \
 JNI_END
 
 WRAPPER_GetField(jobject,  Object,  T_OBJECT)
 WRAPPER_GetField(jboolean, Boolean, T_BOOLEAN)

@@ -1150,11 +1212,11 @@
     functionEnter(thr); \
     IN_VM( \
       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)
 WRAPPER_SetField(jboolean, Boolean, T_BOOLEAN)
 WRAPPER_SetField(jbyte,    Byte,    T_BYTE)

@@ -1174,11 +1236,11 @@
     functionEnter(thr);
     IN_VM(
       jniCheck::validate_class(thr, clazz, false);
     )
     jmethodID result = UNCHECKED()->GetStaticMethodID(env,clazz,name,sig);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 #define WRAPPER_CallStaticMethod(ReturnType,Result) \
 JNI_ENTRY_CHECKED(ReturnType,  \

@@ -1196,11 +1258,12 @@
     ReturnType result = UNCHECKED()->CallStatic##Result##MethodV(env, \
                                                                  clazz, \
                                                                  methodID, \
                                                                  args); \
     va_end(args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("CallStatic"#Result"Method"); \
+    functionExit(thr); \
     return result; \
 JNI_END \
 \
 JNI_ENTRY_CHECKED(ReturnType,  \
   checked_jni_CallStatic##Result##MethodV(JNIEnv *env, \

@@ -1214,11 +1277,12 @@
     ) \
     ReturnType result = UNCHECKED()->CallStatic##Result##MethodV(env, \
                                                                  clazz, \
                                                                  methodID, \
                                                                  args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("CallStatic"#Result"MethodV"); \
+    functionExit(thr); \
     return result; \
 JNI_END \
 \
 JNI_ENTRY_CHECKED(ReturnType,  \
   checked_jni_CallStatic##Result##MethodA(JNIEnv *env, \

@@ -1232,11 +1296,12 @@
     ) \
     ReturnType result = UNCHECKED()->CallStatic##Result##MethodA(env, \
                                                                  clazz, \
                                                                  methodID, \
                                                                  args); \
-    functionExit(env); \
+    thr->set_pending_jni_exception_check("CallStatic"#Result"MethodA"); \
+    functionExit(thr); \
     return result; \
 JNI_END
 
 WRAPPER_CallStaticMethod(jobject,Object)
 WRAPPER_CallStaticMethod(jboolean,Boolean)

@@ -1260,11 +1325,12 @@
       jniCheck::validate_class(thr, cls, false);
     )
     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,
   checked_jni_CallStaticVoidMethodV(JNIEnv *env,
                                     jclass cls,

@@ -1274,11 +1340,12 @@
     IN_VM(
       jniCheck::validate_jmethod_id(thr, methodID);
       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,
   checked_jni_CallStaticVoidMethodA(JNIEnv *env,
                                     jclass cls,

@@ -1288,11 +1355,12 @@
     IN_VM(
       jniCheck::validate_jmethod_id(thr, methodID);
       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,
   checked_jni_GetStaticFieldID(JNIEnv *env,
                                jclass clazz,

@@ -1301,11 +1369,11 @@
     functionEnter(thr);
     IN_VM(
       jniCheck::validate_class(thr, clazz, false);
     )
     jfieldID result = UNCHECKED()->GetStaticFieldID(env,clazz,name,sig);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 #define WRAPPER_GetStaticField(ReturnType,Result,FieldType) \
 JNI_ENTRY_CHECKED(ReturnType,  \

@@ -1318,11 +1386,11 @@
       checkStaticFieldID(thr, fieldID, clazz, FieldType); \
     ) \
     ReturnType result = UNCHECKED()->GetStatic##Result##Field(env, \
                                                               clazz, \
                                                               fieldID); \
-    functionExit(env); \
+    functionExit(thr); \
     return result; \
 JNI_END
 
 WRAPPER_GetStaticField(jobject,  Object,  T_OBJECT)
 WRAPPER_GetStaticField(jboolean, Boolean, T_BOOLEAN)

@@ -1344,11 +1412,11 @@
     IN_VM( \
       jniCheck::validate_class(thr, clazz, false); \
       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)
 WRAPPER_SetStaticField(jboolean, Boolean, T_BOOLEAN)
 WRAPPER_SetStaticField(jbyte,    Byte,    T_BYTE)

@@ -1364,11 +1432,11 @@
   checked_jni_NewString(JNIEnv *env,
                         const jchar *unicode,
                         jsize len))
     functionEnter(thr);
     jstring result = UNCHECKED()->NewString(env,unicode,len);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jsize,
   checked_jni_GetStringLength(JNIEnv *env,

@@ -1376,11 +1444,11 @@
     functionEnter(thr);
     IN_VM(
       checkString(thr, str);
     )
     jsize result = UNCHECKED()->GetStringLength(env,str);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 // Arbitrary (but well-known) tag
 const void* STRING_TAG = (void*)0x47114711;

@@ -1405,11 +1473,11 @@
       }
       // Avoiding call to UNCHECKED()->ReleaseStringChars() since that will fire unexpected dtrace probes
       // 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
 
 JNI_ENTRY_CHECKED(void,
   checked_jni_ReleaseStringChars(JNIEnv *env,

@@ -1440,19 +1508,19 @@
             "not allocated by GetStringChars");
       }
        UNCHECKED()->ReleaseStringChars(env, str,
            (const jchar*) guarded.release_for_freeing());
     }
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(jstring,
   checked_jni_NewStringUTF(JNIEnv *env,
                            const char *utf))
     functionEnter(thr);
     jstring result = UNCHECKED()->NewStringUTF(env,utf);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jsize,
   checked_jni_GetStringUTFLength(JNIEnv *env,

@@ -1460,11 +1528,11 @@
     functionEnter(thr);
     IN_VM(
       checkString(thr, str);
     )
     jsize result = UNCHECKED()->GetStringUTFLength(env,str);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 // Arbitrary (but well-known) tag - different than GetStringChars
 const void* STRING_UTF_TAG = (void*) 0x48124812;

@@ -1488,11 +1556,11 @@
       }
       // Avoiding call to UNCHECKED()->ReleaseStringUTFChars() since that will fire unexpected dtrace probes
       // 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
 
 JNI_ENTRY_CHECKED(void,
   checked_jni_ReleaseStringUTFChars(JNIEnv *env,

@@ -1523,33 +1591,33 @@
             "called on something not allocated by GetStringUTFChars");
       }
       UNCHECKED()->ReleaseStringUTFChars(env, str,
           (const char*) guarded.release_for_freeing());
     }
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(jsize,
   checked_jni_GetArrayLength(JNIEnv *env,
                              jarray array))
     functionEnter(thr);
     IN_VM(
       check_is_array(thr, array);
     )
     jsize result = UNCHECKED()->GetArrayLength(env,array);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jobjectArray,
   checked_jni_NewObjectArray(JNIEnv *env,
                              jsize len,
                              jclass clazz,
                              jobject init))
     functionEnter(thr);
     jobjectArray result = UNCHECKED()->NewObjectArray(env,len,clazz,init);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jobject,
   checked_jni_GetObjectArrayElement(JNIEnv *env,

@@ -1558,11 +1626,12 @@
     functionEnter(thr);
     IN_VM(
       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
 
 JNI_ENTRY_CHECKED(void,
   checked_jni_SetObjectArrayElement(JNIEnv *env,

@@ -1572,20 +1641,21 @@
     functionEnter(thr);
     IN_VM(
       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) \
 JNI_ENTRY_CHECKED(Return, \
   checked_jni_New##Result##Array(JNIEnv *env, \
                                  jsize len)) \
     functionEnter(thr); \
     Return result = UNCHECKED()->New##Result##Array(env,len); \
-    functionExit(env); \
+    functionExit(thr); \
     return (Return) result; \
 JNI_END
 
 WRAPPER_NewScalarArray(jbooleanArray, Boolean)
 WRAPPER_NewScalarArray(jbyteArray, Byte)

@@ -1609,11 +1679,11 @@
                                                                   array, \
                                                                   isCopy); \
     if (result != NULL) { \
       result = (ElementType *) check_jni_wrap_copy_array(thr, array, result); \
     } \
-    functionExit(env); \
+    functionExit(thr); \
     return result; \
 JNI_END
 
 WRAPPER_GetScalarArrayElements(T_BOOLEAN, jboolean, Boolean)
 WRAPPER_GetScalarArrayElements(T_BYTE,    jbyte,    Byte)

@@ -1637,11 +1707,11 @@
       typeArrayOop a = typeArrayOop(JNIHandles::resolve_non_null(array)); \
     ) \
     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)
 WRAPPER_ReleaseScalarArrayElements(T_BYTE,   jbyte,    Byte,    byte)
 WRAPPER_ReleaseScalarArrayElements(T_SHORT,  jshort,   Short,   short)

@@ -1661,11 +1731,12 @@
     functionEnter(thr); \
     IN_VM( \
       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)
 WRAPPER_GetScalarArrayRegion(T_BYTE,    jbyte,    Byte)
 WRAPPER_GetScalarArrayRegion(T_SHORT,   jshort,   Short)

@@ -1685,11 +1756,12 @@
     functionEnter(thr); \
     IN_VM( \
       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)
 WRAPPER_SetScalarArrayRegion(T_BYTE,    jbyte,    Byte)
 WRAPPER_SetScalarArrayRegion(T_SHORT,   jshort,   Short)

@@ -1704,20 +1776,20 @@
                               jclass clazz,
                               const JNINativeMethod *methods,
                               jint nMethods))
     functionEnter(thr);
     jint result = UNCHECKED()->RegisterNatives(env,clazz,methods,nMethods);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jint,
   checked_jni_UnregisterNatives(JNIEnv *env,
                                 jclass clazz))
     functionEnter(thr);
     jint result = UNCHECKED()->UnregisterNatives(env,clazz);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jint,
   checked_jni_MonitorEnter(JNIEnv *env,

@@ -1725,11 +1797,11 @@
     functionEnter(thr);
     IN_VM(
       jniCheck::validate_object(thr, obj);
     )
     jint result = UNCHECKED()->MonitorEnter(env,obj);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jint,
   checked_jni_MonitorExit(JNIEnv *env,

@@ -1737,20 +1809,20 @@
     functionEnterExceptionAllowed(thr);
     IN_VM(
       jniCheck::validate_object(thr, obj);
     )
     jint result = UNCHECKED()->MonitorExit(env,obj);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jint,
   checked_jni_GetJavaVM(JNIEnv *env,
                         JavaVM **vm))
     functionEnter(thr);
     jint result = UNCHECKED()->GetJavaVM(env,vm);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
   checked_jni_GetStringRegion(JNIEnv *env,

@@ -1761,11 +1833,12 @@
     functionEnter(thr);
     IN_VM(
       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,
   checked_jni_GetStringUTFRegion(JNIEnv *env,
                                  jstring str,

@@ -1775,11 +1848,12 @@
     functionEnter(thr);
     IN_VM(
       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 *,
   checked_jni_GetPrimitiveArrayCritical(JNIEnv *env,
                                         jarray array,

@@ -1790,11 +1864,11 @@
     )
     void *result = UNCHECKED()->GetPrimitiveArrayCritical(env, array, isCopy);
     if (result != NULL) {
       result = check_jni_wrap_copy_array(thr, array, result);
     }
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
   checked_jni_ReleasePrimitiveArrayCritical(JNIEnv *env,

@@ -1806,11 +1880,11 @@
       check_is_primitive_array(thr, array);
     )
     // 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*,
   checked_jni_GetStringCritical(JNIEnv *env,
                                 jstring string,

@@ -1818,11 +1892,11 @@
     functionEnterCritical(thr);
     IN_VM(
       checkString(thr, string);
     )
     const jchar *result = UNCHECKED()->GetStringCritical(env, string, isCopy);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
   checked_jni_ReleaseStringCritical(JNIEnv *env,

@@ -1834,11 +1908,11 @@
     )
     /* The Hotspot JNI code does not use the parameters, so just check the
      * string parameter as a minor sanity check
      */
     UNCHECKED()->ReleaseStringCritical(env, str, chars);
-    functionExit(env);
+    functionExit(thr);
 JNI_END
 
 JNI_ENTRY_CHECKED(jweak,
   checked_jni_NewWeakGlobalRef(JNIEnv *env,
                                jobject obj))

@@ -1847,55 +1921,56 @@
       if (obj != NULL) {
         jniCheck::validate_handle(thr, obj);
       }
     )
     jweak result = UNCHECKED()->NewWeakGlobalRef(env, obj);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(void,
   checked_jni_DeleteWeakGlobalRef(JNIEnv *env,
                                   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
 
 JNI_ENTRY_CHECKED(jobject,
   checked_jni_NewDirectByteBuffer(JNIEnv *env,
                                   void *address,
                                   jlong capacity))
     functionEnter(thr);
     jobject result = UNCHECKED()->NewDirectByteBuffer(env, address, capacity);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(void *,
   checked_jni_GetDirectBufferAddress(JNIEnv *env,
                                      jobject buf))
     functionEnter(thr);
     void* result = UNCHECKED()->GetDirectBufferAddress(env, buf);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jlong,
   checked_jni_GetDirectBufferCapacity(JNIEnv *env,
                                       jobject buf))
     functionEnter(thr);
     jlong result = UNCHECKED()->GetDirectBufferCapacity(env, buf);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 JNI_ENTRY_CHECKED(jobjectRefType,
   checked_jni_GetObjectRefType(JNIEnv *env,

@@ -1904,20 +1979,20 @@
     /* validate the object being passed */
     IN_VM(
       jniCheck::validate_object(thr, obj);
     )
     jobjectRefType result = UNCHECKED()->GetObjectRefType(env, obj);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END
 
 
 JNI_ENTRY_CHECKED(jint,
   checked_jni_GetVersion(JNIEnv *env))
     functionEnter(thr);
     jint result = UNCHECKED()->GetVersion(env);
-    functionExit(env);
+    functionExit(thr);
     return result;
 JNI_END