카테고리 없음

[Android] Java Native Interface[JNI] - local reference table overflow error

Jun.LEE 2024. 3. 19. 16:12

알고리즘 연산의 성능 개선을 위하여 JNI를 사용하는 중에 문제가 발생했다.

extern "C" JNIEXPORT void JNICALL
operate(
        JNIEnv* env,
        jobject thiz,
        jobject nodes,
        jobject edges
) {
	jobject* ithNode = (jobject*) malloc(4);
	while(op) {
    	    for (jint i = 0; i < nodeLen; i++) {
        	*ithNode = env->CallObjectMethod(nodes, java_util_ArrayList_get, i);
            
            /** 생략 */
    }
}

 

내가 사용한 코드를 대략적으로 나타낸 모습이다.

ArrayList<Node> type의 nodes를 함수의 파라미터로 받고 있고

for문 내에서 리스트를 순차적으로 방문하며 연산을 한다.

그리고 while문으로 이 과정을 반복하는 알고리즘이다.

 

Node 오브젝트의 주소를 정의하기 위해서 메모리 4바이트를 할당한 모습을 볼 수 있고

그 주소를 계속해서 재사용하고 있다.

 

이 코드를 실행시키고 while문의 사이클이 2, 3천번 지나면

Local Reference Table Overflow Error가 발생한다.

 

JVM memory area를 살펴보면 다음과 같다.

JVM Memory Architecture

 

JNI에서 CallObjectMethod를 사용하면

Local Reference와 자바 객체를 mapping하게 된다.

Local Reference Table에 자바 객체의 주소를 가지고 있는 것으로 C++ 내에서 

자바 객체를 간접적으로 사용하는 효과를 볼 수 있다.

 

이 부분이 에러가 발생하도록 하는 부분이다.

 

ArrayList<Node>의 특정 원소를 jobject* ithNode의 할당된 메모리에 정의하면 

Local Reference Table에 해당 자바의 객체[Node]와 ithNode 간의 mapping이 등록된다.

이후 다른 원소를 정의하게 되면 Local Refenence Table에 mapping이 유지된 채로 

또 다른 mapping이 등록되게 되는 것이다.

 

이렇게 Local Reference Table은 점점 채워져가고 최대 사이즈를 초과하게 되면 

오버플로우 에러를 발생시키게 된다.

 

일반적으로 함수를 리턴하게 되면 해당 테이블은 모두 GC[Garbage Collector]의 대상이 되므로

작성하는 사람이 신경쓸 필요가 없다고 한다.

하지만 나의 사례처럼 반복문을 사용하는 경우에는 신경써줄 필요가 있다.

 

JNI로 반복문을 작성할때는 사이클 마다 Local Refererence를 반드시 release해주자!