2012-08-23 18 views
5

Cuando ejecuto el código, me da un error "No se pudo añadir a JNI mesa ref local tiene 512 entradas"JNI desbordamiento de mesa, incluso después de DeleteLocalRef

Este es mi código:

jstring pJNIData = pJNIEnv->NewStringUTF (variables[0].GetStringValue()); 

pJNIEnv->CallStaticVoidMethod (pJNIActivityClass, pJNIMethodIDStartTime, pJNIData) ; 

pJNIEnv->DeleteLocalRef(pJNIData); 

He leído varias sugerencias, ¡pero ninguna de ellas funciona! A pesar de DeleteLocalRef, no funciona. La función se utiliza en un generador de perfiles que llama literalmente todas las funciones ...

+0

¿Estás seguro de que este es el código que llena tu tabla de referencias? ¿El problema desaparece cuando usas un pJNIData codificado constante? –

+0

@vtmarvin No estoy seguro. ¿Esto hace una diferencia en la tabla de referencia? -> JNIEnv * pJNIEnv = profilerGetJNIEnv(); si (pJNIEnv!) \t \t { \t \t \t LOGE ("Profiler ERROR: entorno Java de falta (nulo)"); \t \t \t return; \t \t} –

Respuesta

2

he visto este método cuando una llamada JNI código Java (en mi caso, el método no era estático). Según tengo entendido, las referencias locales no utilizados son no borrado automáticamente cuando un método Java se llama desde JNI (quiero decir, hasta que la función JNI de nivel superior devoluciones).

IIRC bien ya existía información acerca de los objetos de memoria en el registro, o yo podría añadir un poco de registro; de esa información identifiqué elementos de basura que no mencioné antes. Eran dos matrices y una clase, creadas en llamadas posteriores pero no recogidas.

// in a function that calls a Java method from JNI 
jbyteArray srcArray = env->NewByteArray(len); 
jclass cls = env->FindClass("com/something/MyClass"); 
jmethodID mid = env->GetMethodID(cls, "mymethod", "([BI)[B"); 
jbyteArray resArray = (jbyteArray)env->CallObjectMethod(obj, mid, srcArray, XXXX); 

... 
env->DeleteLocalRef(cls); 
env->DeleteLocalRef(resArray); 
env->DeleteLocalRef(srcArray); 
// no need to do anything with mid 

Tenga en cuenta que aunque estas tres referencias locales se obtuvieron de manera diferente, todas estaban dando vueltas.

Enlace de interés: http://www.netmite.com/android/mydroid/dalvik/docs/jni-tips.html#local_vs_global_references (o encontrar la máquina virtual Dalvik Dalvik docs/docs/JNI-tips.html y localizar la sección "Referencias local frente Global")

cada objeto que devuelve es una JNI "referencia local". Esto significa que es válido por la duración del método nativo actual en el hilo actual. Incluso si el objeto en sí continúa vivo después de que el método nativo retorna, la referencia no es válida. Esto se aplica a todas las subclases de jobject, incluidos jclass y jarray. [...] Nota: el método y los ID de campo son solo identificadores de 32 bits, no referencias de objeto, y no deben pasarse a NewGlobalRef. Los punteros de datos sin formato devueltos por funciones como GetStringUTFChars y GetByteArrayElements tampoco son objetos.

0

pensé que iba a hacer mella en sólo en caso de cualquier otra persona se encuentra con este problema. ¡Este es un caso raro que me mantuvo confundido durante horas!

Ok, así que tienen una aplicación NDK y el código Java que se llama está dentro de un APK que se carga en tiempo de ejecución. No tengo idea si la carga en tiempo de ejecución afecta esto de alguna manera, pero pensé que debería mencionarlo.

Ahora en un método C++ utilizo find class y getmethodid para obtener el constuctor en un HashMap y llamarlo para obtener una nueva instancia de HashMap. Luego llené el HashMap desde el lado de C++ usando llamadas jni. Hasta aquí todo bien. Luego paso el HashMap al código java y, una vez más, todo está funcionando como se esperaba. Una vez que el código de Java ha regresado, llamo DeleteLocalRef en HashMap. No se generan errores, pero la referencia no se elimina.

Esto solo apareció cuando finalmente ejecuté más de 512 referencias locales (desde varias llamadas a esta función) y el error de volcado mostró que los últimos 10 artículos en la tienda localref eran casi todos HashMaps. Entiendo que el GC no recogería estas referencias al final del método ya que estoy haciendo una aplicación ndk multiproceso. Sin embargo, DeleteLocalRef debería haber funcionado.

La solución: Al final me di cuenta de que la creación de la HashMap de una llamada JNI a un método Java que escribí estaba bien, y la referencia era entonces free'able. Parece una locura tener una función java que, literalmente, solo devuelve un nuevo HashMap, pero funcionó así que por el momento estoy viviendo con él :)

Cuestiones relacionadas