为了账号安全,请及时绑定邮箱和手机立即绑定

Release<Type>ArrayElements 在 JNI 调用期间实际上没有从堆中释放内存?

Release<Type>ArrayElements 在 JNI 调用期间实际上没有从堆中释放内存?

小唯快跑啊 2023-06-21 15:31:45
我正在开发一个与一些本机 C++ 代码 (JNI) 接口的 Android 应用程序。在 Java 方面,我将一个查找表(双精度数组)和两个 Open-CV 矩阵传递给 JNI(通过引用),然后使用 C++ 处理这些矩阵。虽然 JNI 函数调用在前 15~20 次有效,但应用程序很快就会崩溃并重新启动。我 99% 确信这是我没有正确释放堆上内存的问题。我查看了 Profiler 以检查内存的情况,发现每个 JNI 函数调用的内存使用量都在持续增加。大部分内存分配似乎都在 Native 部分,您可以在下图中看到这种增加(增加与对 JNI 函数的调用一致)。extern "C" JNIEXPORT void JNICALLJava_com_mygroup_productName_ImgProcUtils_interpVals(        JNIEnv *env,        jobject /* this */,        jlong addrKSqrd,        jint nRows,        jint nCols,        jdoubleArray yTaucVal,        jlong addrTauc) {    cv::Mat& kSqrd = *(cv::Mat*)addrKSqrd;    cv::Mat& Tauc = *(cv::Mat*)addrTauc;    jboolean isCopy;    jdouble *elem = env->GetDoubleArrayElements(yTaucVal, &isCopy);    float pixel;    for (int i = 0; i < nRows; i++) {        for (int j = 0; j < nCols; j++) {            pixel = kSqrd.at<float>(i, j);            int value = (int)round(pixel * 65535);            if (value < 0) {                value = 0;            } else if (value > 65535) {                value = 65535;            }            Tauc.at<float>(i,j) = (jfloat)elem[value];        }    }    env->ReleaseDoubleArrayElements(yTaucVal, elem, JNI_ABORT);}正如您所看到的,我正在释放 for 循环之前“获取”的双数组,但似乎我们仍然有未释放的内存。我还需要做其他事情才能正确释放内存吗?我还需要发布任何其他数据吗?
查看完整描述

1 回答

?
MMMHUHU

TA贡献1834条经验 获得超8个赞

我怀疑是释放问题。


如果我有像这样的超级简单的Java代码


package recipeNo026;


public class PassArray {


  public static native void passDoubleArray(double[] array);

  static { System.loadLibrary("PassArray"); }


  public static void main(String[] args) throws Exception {

    for(int i=0; i<100; i++) {

      double[] doubleArray = new double[1_000_000_000];

      passDoubleArray(doubleArray);

      Thread.sleep(1000);

    }

  }

}

JNI本机代码除了调用stuff之外没有其他调用


JNIEXPORT void JNICALL Java_recipeNo026_PassArray_passDoubleArray

  (JNIEnv * env, jclass obj, jdoubleArray array) {


  printf ("Double array\n");

  jboolean isCopy;

  jdouble *doubleBody = (*env)->GetDoubleArrayElements(env, array, &isCopy);

  (*env)->ReleaseDoubleArrayElements(env, array, doubleBody, JNI_ABORT); 

}

Java 堆和本机代码的内存消耗似乎都非常稳定。您可以看到代码继续运行时本机内存是如何分配和释放的。

//img1.sycdn.imooc.com//6492a78b0001ff0205930394.jpg

//img1.sycdn.imooc.com//6492a7960001655405990119.jpg

我肯定会开始寻找从您的包装器调用的部分代码中的泄漏JNI。


另外,请注意这样一个事实,即使您根本不调用本机代码(例如JNI),本机内存也会增长。毕竟,Java 在某些时候必须使用malloc它自己的堆进行分配。看看这里:


public static void main(String[] args) throws Exception {


    int size = 10;


    double [][] array = new double[100][1];


    for(int i=0; i<100; i++) {

      array[i] = new double[size];

      size = size * 2;

      System.out.println("Allocating: " + size);

      Thread.sleep(1000);

    }


  }

没有JNI电话了。现在,让我们运行该应用程序。


> java -Xmx4G -Xms512m -Djava.library.path=:./lib -cp target recipeNo026.PassArray

library: :./lib

Allocating: 20

Allocating: 40

Allocating: 80

Allocating: 160

Allocating: 320

Allocating: 640

Allocating: 1280

Allocating: 2560

Allocating: 5120

Allocating: 10240

Allocating: 20480

Allocating: 40960

Allocating: 81920

Allocating: 163840

Allocating: 327680

Allocating: 655360

Allocating: 1310720

Allocating: 2621440

Allocating: 5242880

Allocating: 10485760

Allocating: 20971520

Allocating: 41943040

Allocating: 83886080

Allocating: 167772160

Allocating: 335544320

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

    at recipeNo026.PassArray.main(PassArray.java:25)

让我们看一下 Java 进程的本机内存消耗(随着时间的推移)。

//img1.sycdn.imooc.com//6492a7a700010afa13030274.jpg

查看完整回答
反对 回复 2023-06-21
  • 1 回答
  • 0 关注
  • 171 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信