title: Why do I need to release global references created in JNI native functions?
body: <p>I have a Java class with native functions implemented in C++ which is called <code>nz.ac.unitec.BigInteger</code>. The native implementation of <code>nz.ac.unitec.BigInteger</code> is simple, just wraps the <code>java.math.BigInteger</code> and call its constructor and <code>add</code>, <code>subtract</code>, <code>multiply</code> ... functions. A field <code>mNativeContext</code> in <code>nz.ac.unitec.BigInteger</code> is used to store a global reference to a <code>java.math.BigInteger</code> object. The object has a finalizer that should destroy the global reference when the object is garbage collected so I am not leaking global references.</p>
  
  <p>When I runned a simple test loop to create a number of <code>nz.ac.unitec.BigInteger</code> objects without explicitly releasing the created objects, a JNI error was reported as following log (LOG1). But if I release the created objects explicitly before leaving the test function, the stress test can be executed successfully.</p>
  
  <p>Why is the JVM running out of global references when I have a finalizer to delete them when the objects are garbage collected?</p>
  
  <p>LOG1:</p>
  
  <pre><code>F/art     (10730): art/runtime/indirect_reference_table.cc:113] JNI ERROR (app bug): global reference table overflow (max=51200)
  
  F/art     (10730): art/runtime/indirect_reference_table.cc:113] global reference table dump:
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]   Last 10 entries (of 51200):
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]     51199: 0x12e88790 java.math.BigInteger
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]     51198: 0x12e85490 java.math.BigInteger
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]     51197: 0x12e81790 java.math.BigInteger
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]     51196: 0x12e7e760 java.math.BigInteger
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]     51195: 0x12e7ab20 java.math.BigInteger
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]     51194: 0x12e77790 java.math.BigInteger
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]     51193: 0x12e73a90 java.math.BigInteger
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]     51192: 0x12e71af0 java.math.BigInteger
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]     51191: 0x12e6dd60 java.math.BigInteger
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]     51190: 0x12e6b9a0 java.math.BigInteger
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]   Summary:
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]      1456 of java.math.BigInteger (1456 unique instances)
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]         2 of android.opengl.EGLDisplay (2 unique instances)
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]      1889 of java.math.BigInteger (1889 unique instances)
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]         1 of java.lang.String
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]        27 of java.math.BigInteger (27 unique instances)
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]         1 of java.lang.String
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]      3771 of java.math.BigInteger (3771 unique instances)
  F/art     (10730): art/runtime/indirect_reference_table.cc:113]         1 of dalvik.system.PathClassLoader
  F/art     (10730): art/runtime/runtime.cc:284] Runtime aborting...
  F/art     (10730): art/runtime/runtime.cc:284] Aborting thread:
  </code></pre>
  
  <p>Calculator.java</p>
  
  <pre><code>package nz.ac.unitec.calculator;
  
  public class MainActivity extends AppCompatActivity
  {
      protected void onCreate(Bundle savedInstanceState)
      {
          Random ra = new Random();
          for(int i=0; i&lt;6000000; ++i) {
              testCreateFinalize(ra);
              int m = ra.nextInt();
              int n = 8;
              int re = m + n;
              Log.i("MainActivity", "re=" + re);
              //BigInteger result = l.subtract(r);
          }
  
      private void testCreateFinalize(Random ra)
      {
          BigInteger l = new BigInteger("100", 10);
  
          BigInteger r = new BigInteger("200", 10);
          //l.release();      when adding this two code lines, the test is ok
          //r.release();;
      }
  }
  </code></pre>
  
  <p>BigInteger.java</p>
  
  <pre><code>package nz.ac.unitec.mathutils;
  public class BigInteger
  {
      static {
          System.loadLibrary("BigInteger_jni");
          native_init();
      }
  
      private long mNativeContext;
  
      private BigInteger()
      {
          mNativeContext = 0;
          native_setup();
      }
  
      public BigInteger(String val, int radix)
      {
          mNativeContext = 0;
          native_setup_bystring(val, radix);
      }
  
      public void release() {
          native_finalize();
      }
  
      protected void finalize() {
          native_finalize();
      }
  
  
      private static native final void native_init();
      private native final void native_setup();
      private native final void native_setup_bystring(String val, int radix);
      private native final void native_finalize();
      public native String  toString(int radix);
  
  
      public native BigInteger add(BigInteger rval);
      public native BigInteger multiply(BigInteger rval);
      public native BigInteger subtract(BigInteger rval);
  
  }
  </code></pre>
  
  <p>Native Code</p>
  
  <pre><code>static jobject getNativeBigInteger_l(JNIEnv* env, jobject thiz)
  {
      return reinterpret_cast&lt;jobject&gt; (env-&gt;GetLongField(thiz, fields.context));   //reinterpret_cast&lt;jobject&gt;
  }
  
  static void setNativeBigInteger_l(JNIEnv* env, jobject thiz, jobject bi)
  {
      env-&gt;SetLongField(thiz, fields.context, reinterpret_cast&lt;jlong&gt;(bi)); //reinterpret_cast&lt;jlong&gt;
  }
  
  JNIEXPORT void JNICALL Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring
          (JNIEnv *env, jobject thiz, jstring val, jint radix)
  {
  
      jclass cls = env-&gt;FindClass(gBuiltinClassBigInteger);
      ALOGV("-Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring env-&gt;FindClass(%s) return (%0x)",
            gBuiltinClassBigInteger, cls);
  
      if (cls == NULL) {
          ALOGE("-Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring env-&gt;FindClass(%s) return NULL", gBuiltinClassBigInteger);
          return;
      }
  
      jmethodID constructor = env-&gt;GetMethodID(cls, "&lt;init&gt;", "(Ljava/lang/String;I)V");
      if (constructor == NULL) {
          env-&gt;DeleteLocalRef(cls);
          ALOGE("-Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring env-&gt;GetMethodID(%s) return NULL", "&lt;init&gt;");
          return;
      }
  
      jobject jobj = env-&gt;NewObject(cls, constructor, val, radix);
      ALOGV("-Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring env-&gt;NewObject return (%0x)",
            jobj);
      if (NULL == jobj) {
          env-&gt;DeleteLocalRef(cls);
          ALOGE("-Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring env-&gt;NewObject return NULL");
          return;
      }
  
      jobject gjobj = env-&gt;NewGlobalRef(jobj);
      ALOGV("-Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring env-&gt;NewGlobalRef return (%0x)", gjobj);
      setNativeBigInteger_l(env, thiz, gjobj);
      env-&gt;DeleteLocalRef(jobj);
      //env-&gt;DeleteLocalRef(cls);
  }
  
  JNIEXPORT void JNICALL
  Java_nz_ac_unitec_mathutils_BigInteger_native_1finalize
          (JNIEnv *env, jobject thiz)
  {
      ALOGV("+native_finalize");
      jobject obj = getNativeBigInteger_l(env, thiz);
      ALOGV("-Java_nz_ac_unitec_mathutils_BigInteger_native_1finalize getNativeBigInteger_l return (%0x)",
                            obj);
      if (obj == NULL) {
          ALOGE("-native_finalize getNativeBigInteger_l NULL");
          return;
      }
      env-&gt;DeleteGlobalRef(obj);
      setNativeBigInteger_l(env, thiz, NULL);
      ALOGV("-native_finalize");
  }
  
  
  static JNINativeMethod gMethods[] = {
          {"native_init",         "()V",
                  (void *)Java_nz_ac_unitec_mathutils_BigInteger_native_1init},
  
          {"native_setup",    "()V",
                  (void *)Java_nz_ac_unitec_mathutils_BigInteger_native_1setup},
  
          {"native_setup_bystring", "(Ljava/lang/String;I)V",
                  (void*)Java_nz_ac_unitec_mathutils_BigInteger_native_1setup_1bystring},
  
  
          {"native_finalize",     "()V",
                  (void *)Java_nz_ac_unitec_mathutils_BigInteger_native_1finalize},
  
          {"add",     "(Lnz/ac/unitec/mathutils/BigInteger;)Lnz/ac/unitec/mathutils/BigInteger;",
           (void *)Java_nz_ac_unitec_mathutils_BigInteger_add},
  
          {"multiply","(Lnz/ac/unitec/mathutils/BigInteger;)Lnz/ac/unitec/mathutils/BigInteger;",
                  (void *)Java_nz_ac_unitec_mathutils_BigInteger_multiply},
          {"subtract","(Lnz/ac/unitec/mathutils/BigInteger;)Lnz/ac/unitec/mathutils/BigInteger;",
                  (void *)Java_nz_ac_unitec_mathutils_BigInteger_subtract},
          {"toString","(I)Ljava/lang/String;",
                  (void *)Java_nz_ac_unitec_mathutils_BigInteger_toString}
  };
  
  int register_Java_nz_ac_unitec_mathutils_BigInteger(JNIEnv *env)
  {
      return jniRegisterNativeMethods(env, gClassBigInteger, gMethods, NELEM(gMethods));
  }
  </code></pre>