Android JNI OutOfMemoryError

时间:2016-11-03 09:49:51

标签: java android opencv java-native-interface out-of-memory

我的应用程序通过3321次迭代执行以下任务:

  1. 从SD卡加载图像(每次迭代中的不同图像)
  2. 调整图片大小
  3. 将原生的1-d数据传递给Android
  4. 将1-d数据从Android传递给本机(进行一些计算)
  5. 通过logcat,我收到了以下消息:

    Progress: 1 / 3321
    Progress: 2 / 3321
    .
    .
    .
    Grow heap (frag case) to 5.597MB for 206132-byte allocation
    .
    .
    .
    Progress: X / 3321
    Progress: X+1 / 3321
    .
    .
    .
    Grow heap (frag case) to 64.000MB for 206132-byte allocation
    .
    .
    .
    FATAL EXCEPTION: Thread-451 Process: com.cv.myapp, PID: 15006
    java.lang.OutOfMemoryError 
    at com.cv.myapp.SecondModule.loadImg(Native Method)
    

    任务1,2和3由我的第二个模块完成,任务4由我的第一个模块完成

    在第二个模块中(用于加载图像,调整大小并传递1-d数据):

    /* Global variable */
    
    Mat img;
    
    /* JNI function */
    
    JNIEXPORT jfloatArray JNICALL
    Java_cv_myapp_SecondModule_loadImg(JNIEnv* env, jclass thiz, 
        jstring imgPath, jint height, jint width) {
        // Retrieve the string passed form JAVA
        const char* img_path = env->GetStringUTFChars(imgPath, 0);
    
        // Load image into OpenCV Mat object
        img = imread(img_path, IMREAD_COLOR);
    
        // Release the string
        env->ReleaseStringUTFChars(imgPath, img_path);
    
        // Try to resize the image if the height or width does not
        // match the given parameters
        if ( (img.rows != height) || (img.cols != width) ) 
            resize( img, img, Size(height, width) );
    
        // Get the float pointer that point to Mat data
        float* data = (float*) img.data;
    
        // Return the data from native to JAVA
        jfloatArray res = env->NewFloatArray(height * width);
    
        if (res == NULL)
            return NULL;
    
        env->SetFloatArrayRegion(res, 0, height * width, data);
    
        return res;
    }
    

    在我的第一个模块中执行任务4的本机函数:

    JNIEXPORT void JNICALL
    Java_cv_my_app_FirstModule_processImg(JNIEnv* env, jclass thiz,
        jfloatArray img) {
        // Get the image data (float array) passed from JAVA
        jboolean is_copied;
        jint img_size = env->GetArrayLength(img);
        jfloat* img_data = env->GetFloatArrayElements(img, &is_copied);
    
        // Do some calculation on variable img_data, 
        // and storing the result to the global variable.
    
        // Release the image data
        if (is_copied == JNI_TRUE)
            env->ReleaseFloatArrayElements(img, img_data, JNI_ABORT);
    }
    

    在Android中:

    FirstModule first_module = new FirstModule();
    SecondModule second_module = new SecondModule();
    
    // Load image paths into variable imgSet
    // Each image path can be retrieved by imgSet[i]
    
    // 3321 iterations
    for (int i=0 ; i<3321 ; ++i) {
        first_module.processImg(    
            second_module.loadImg( imgSet[i] )
        );
    }
    

    我想OutOfMemoryError可能是由于在本机和JAVA之间传输数据,但我不确定(错误可能是由于其他原因)。 我的代码出了什么问题? 非常感谢!

    编辑:

    我替换了

    if (is_copied == JNI_TRUE)
        env->ReleaseFloatArrayElements(img, img_data, JNI_ABORT);
    

    env->ReleaseFloatArrayElements(img, img_data, JNI_ABORT);
    

    ,并打印以下日志。似乎发生了垃圾收集。

    Progress: 301 / 3321
    
    Clamp target GC heap from 65.488MB to 64.000MB
    Grow heap (frag case) to 64.000MB for 206132-byte allocation
    
    Progress: 302 / 3321
    Progress: 303 / 3321
    
    Forcing collection of SoftReferences for 206132-byte allocation
    GC_BEFORE_OOM freed 61959K, 96% free 3192K/65408K, paused 21ms, total 24ms
    
    Progress: 304 / 3321
    Progress: 305 / 3321
    
    Grow heap (frag case) to 4.272MB for 206132-byte allocation
    
    Progress: 306 / 3321
    .
    .
    .
    

    垃圾收集也在603次迭代后发生。

    Progress: 603 / 3321
    
    Clamp target GC heap from 64.563MB to 64.000MB
    Grow heap (frag case) to 64.000MB for 206132-byte allocation
    
    Progress: 604 / 3321
    Progress: 605 / 3321
    Progress: 606 / 3321
    
    Forcing collection of SoftReferences for 206132-byte allocation
    GC_BEFORE_OOM freed 61219K, 87% free 3185K/24008K, paused 34ms, total 38ms
    Progress: 607 / 3321
    .
    .
    .
    

    但是,在下面的日志打印后应用程序崩溃了

    Progress: 642 / 3321
    Grow heap (frag case) to 12.474MB for 206132-byte allocation
    Progress: 643 / 3321
    

    并且没有其他日志跟随日志&#34;进度:643/3321&#34;

    EDIT2:

    我添加了

    LOGI("Release Mat.");
    img.release();
    LOGI("Release Mat ... ok");
    

    return res;
    

    在第二个模块中。

    日志显示

    I/SecondModuleJNI: Release Mat.
    I/SecondModuleJNI: Release Mat ... ok
    I/FirstModuleJNI: Progress: 1 / 3321
    I/dalvikvm-heap: Grow heap (frag case) to 4.816MB for 206132-byte allocation
    
    在应用程序崩溃之前

1 个答案:

答案 0 :(得分:1)

if (is_copied == JNI_TRUE)
    env->ReleaseFloatArrayElements(img, img_data, JNI_ABORT);
无论是否复制,都应始终调用

ReleaseFloatArrayElements()。否则,对数组的引用计数可能不会减少,也不能进行垃圾回收。