引言
在Android开发中,常会遇到 local reference table overflow的错误。原因是从java代码进入jni层的本地代码调用时,Dalvik就会创建一张local reference表来存储local reference, 这张表的表项数有最大限制。一般为512个。当表项数超过最大值限制时,Dalvik就会抛出异常。
下面这段代码就会导致溢出 1
2
3
4
5
6
jclass strinClass = NULL ;
for ( int i = 0 ; i < 800 ; i ++ )
{
stringClass = env -> FindClass ( "java/lang/String" );
.....
}
什么是Local References?
大多数的JNI函数都创建了Local reference(以下简称为LR )。 比如 NewObject, NewString…. 和 FindClass 等。
一个LR 只有在创建它的的一个局部作用域内有效。在超出这个作用域之后,就可能会被释放。 所有的LR 在本地函数调用时创建的,在这个本地函数返回时就会被释放。
你不能把一个Local reference 保存在静态变量中,然后在后面继续调用,这是不安全的。
错误的例子 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* This code is illegal */
jstring
MyNewString ( JNIEnv * env , jchar * chars , jint len )
{
static jclass stringClass = NULL ;
jmethodID cid ;
jcharArray elemArr ;
jstring result ;
if ( stringClass == NULL ) {
stringClass = ( * env ) -> FindClass ( env ,
"java/lang/String" );
if ( stringClass == NULL ) {
return NULL ; /* exception thrown */
}
}
/* It is wrong to use the cached stringClass here,
because it may be invalid. */
cid = ( * env ) -> GetMethodID ( env , stringClass ,
"<init>" , "([C)V" );
...
elemArr = ( * env ) -> NewCharArray ( env , len );
...
result = ( * env ) -> NewObject ( env , stringClass , cid , elemArr );
( * env ) -> DeleteLocalRef ( env , elemArr );
return result ;
}
JNIEXPORT jstring JNICALL
Java_C_f ( JNIEnv * env , jobject this )
{
char * c_str = ...;
return MyNewString ( c_str );
}
//Java 中
... = C . f ();
... = C . f ();
假设我调用了两次Java_C_f。 其中调用了MyNewString, MyNewString的想法是把stringClass这个LR 初始化一次后保存起来,下次可以继续用。但根据JNI的规则。在MyNewString返回时,所有的LR 都会被释放。所以第二次调用时stringClass就是Invalid的。
虽然VM会在本地函数返回时,释放所有的LR ,但我们也可以显示的控制LR 的释放。LR 引用的对象只有在,LR 无效的时候才会被GC回收,在MyNewString函数中调用的DeleteLocalRef 释放中间的变量elemArr,这样GC可以立即回收这个LR 不然elemArr这个LR 会等到本地函数MyNewString返回时才被回收。
LR 只能在创建它的线程中使用,在其它的线程中使用它是错误的。所以用一个全局变量保存在其它线程中用是不可行的。
释放引用
错误的例子 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//1.FindClass
//例如,
jclass ref = ( env ) -> FindClass ( "java/lang/String" );
env -> DeleteLocalRef ( ref );
2. NewString / NewStringUTF / NewObject / NewByteArray
//例如,
jstring ( * NewString )( JNIEnv * , const jchar * , jsize );
const jchar * ( * GetStringChars )( JNIEnv * , jstring , jboolean * );
void ( * ReleaseStringChars )( JNIEnv * , jstring , const jchar * );
jstring ( * NewStringUTF )( JNIEnv * , const char * );
const char * ( * GetStringUTFChars )( JNIEnv * , jstring , jboolean * );
void ( * ReleaseStringUTFChars )( JNIEnv * , jstring , const char * );
env -> DeleteLocalRef ( ref );
3. GetObjectField / GetObjectClass / GetObjectArrayElement
jclass ref = env -> GetObjectClass ( robj );
env -> DeleteLocalRef ( ref );
4. GetByteArrayElements
jbyte * array = ( * env ) -> GetByteArrayElements ( env , jarray , & isCopy );
( * env ) -> ReleaseByteArrayElements ( env , jarray , array , 0 );
const char * input = ( * env ) -> GetStringUTFChars ( env , jinput , & isCopy );
( * env ) -> ReleaseStringUTFChars ( env , jinput , input );
参考
Local and Global References
The Java ™ Native Interface Programmer’s Guide and Specification
http://mysuperbaby.iteye.com/blog/1603817