💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
通过前几节的学习,现在我们准备进入管理JNI引用的规则这部分了,我们的目标是消除不必要的内存占用的对象暂存。 通常有两种Native代码,一种是Java代码的Native实现,另一种是可能被用在任何地方的工具函数。 当书写直接实现Native函数的代码的时候,你需要注意不要在循环中创建过多的局部引用,以及不要再死循环代码(事件派发类函数)中创建不必要的局部引用。Native代码中一定不要有引起全局引用或全局弱引用持续增长的代码,因为他们不会再函数返回之后被自动销毁。 当书写Native工具函数的时候你必须小心,不要在任何执行分支上泄漏局部引用,因为一个工具函数可能在外部被多次重复调用,任何没有必要的引用创建都可能造成内存溢出。 当一个返回基础类型的工具函数被调用的时候,它一定不能对累积产生额外的局部,全局或者全局弱引用有副作用。 当一个返回对象引用类型的函数被调用时,除了它返回的对象引用, 它一定不能累积产生额外的局部,全局或者全局弱引用。 工具函数中处于缓存的目的创建一些全局或者全局弱引用是可以接受的,因为仅仅会在函数第一次被调用的时候这些引用才会被创建。 如果一个工具函数返回一个引用,你应当保证你所返回的引用类型是规范明确的, 不能有的分支返回局部引用,有的分支返回全局引用。调用者需要明确函数返回的引用类型,这样他们才能正确的管理这些引用的声明周期。例如,如下的代码重复调用一个工具函数GetInfoString。我们需要知道GetInfoString返回的引用的类型来做迭代。 while(JNI_TRUE) { jstring infoString = GetInfoString(info); ... /* process infoString */ ??? /* we need to call DeleteLocalRef, DeleteGlobalRef or DeleteWeakGlobalRef depending on the type of reference returned by GetInfoString. */ } NewLobalRef函数可以用来保证返回一个局部引用。 Push/PopLocalFrame函数通常用来方便的管理局部引用的声明周期,这对函数是非常高效的,所以强烈建议你使用它们。 如果你在函数入口的时候调用PushLocalFrame,记得在所有函数退出分支里调用PopLocalFrame,例如,如下函数有一个PushLocalFrame调用但是需要多个PopLocalFrame调用。 ~~~ jobject f(JNIEnv * env, ...){ jobject result; if((*env)->PushLocalFrame(env, 10) < 0) { /* frame not pushed , no PopLocalFrame needed */ return NULL; } result = ...; if(...) { /* rememeber to pop local frame before return */ result = (*env)->PopLocalFrame(env, result); return result; } ... result = (*env)->PopLocalFrame(env, result); /* normal return */ return result; } ~~~ PopLocalFrame调用失败将会导致未定义的行为,比如虚拟机崩溃等。 以上示例也展示了为什么为PopLocalFrame指定第二个参数有的时候是有用的。**result**局部引用是在PushLocalFrame之后新创建的帧(作用域,类似于堆栈的帧)中创建的,PopLocalFrame在pop出最上层帧之前将它的第二个参数**result**转换成为前一个帧中的一个全新的局部引用。