Android NDK开发系列教程5:局部引用,全局引用,弱全局引用

简介: 终于建了一个自己个人小站:https://huangtianyu.gitee.io,以后优先更新小站博客,欢迎进站,O(∩_∩)O~~1. 简介从Java虚拟机创建的对象当传入到native层时会产生一个引用,在进行垃圾回收时如果有native的引用,改对象同样也不会被回收。

终于建了一个自己个人小站:https://huangtianyu.gitee.io,以后优先更新小站博客,欢迎进站,O(∩_∩)O~~

1. 简介

从Java虚拟机创建的对象当传入到native层时会产生一个引用,在进行垃圾回收时如果有native的引用,改对象同样也不会被回收。在native引用中分局部引用和全局引用。

1.1 局部引用

局部引用又称本地引用,大多数见到的引用都是局部引用,例如通过NewLocalRef和各种JNI接口创建(FindClass、NewObject、GetObjectClass和NewCharArray等),局部引用只会在本次native调用中有效,当本次调用结束后该引用即被自动释放。局部引用会阻止GC进行回收。同时也可以调用DeleteLocalRef函数来手动释放(比如在循环里面用到了局部引用而退出循环没有使用该局部引用,那么就需要在循环中释放该局部引用)。通常使用NewObject创建的实例返回的也是局部引用。千万不要把局部引用保存为c++的全局变量或者把它定义为静态变量,局部引用的有效期是一次Java本地调用。
JNI提供了一系列函数来管理局部引用的生命周期。这些函数包括:EnsureLocalCapacity、NewLocalRef、PushLocalFrame、PopLocalFrame、DeleteLocalRef。

    //申请扩充局部引用的最大个数限制,返回值等于0的时候表示成功,>0时表示内存溢出。默认至少16个局部引用可以使用,引用数超出时报FatalError
    jint (*EnsureLocalCapacity)(JNIEnv*, jint);

    /* PushLocalFrame是一个创建本地引用新作用域的有用函数,这使得PushLocalFrame函数可以释放其使用的框架中所有已分配的本地引用。当该函数被调用时,本地引用的最低数量将在本框架中被创建。该函数如果执行成功则返回0,如果由于错误抛出一个OutOfMemoryException,则返回一个负值。*/   
    jint PushLocalFrame(jint capacity);

    /*PopLocalFrame函数释放当前框架中的所有本地引用(弹出一个框架)。因为存储该函数的结果(返回值)可能会导致在即将被弹出的框架中创建一个本地引用,该函数接收一个可以导致引用在当前框架被弹出之后的最高框架中创建的参数。这就确保可以维护一个存储PopLocalFrame函数结果的引用。*/
    jobject PopLocalFrame(jobject result);

PushLocalFrame为当前函数中局部引用创建了一个引用堆栈,在每遍历一次调用(*env)->GetObjectArrayElement(env, arr, i);返回一个局部引用时,JVM会自动将该引用压入当前局部引用栈中。而PopLocalFrame负责将栈中所有引用释放。这样一来,Push/PopLocalFrame函数对提供了对局部引用生命周期更方便的管理,不用再去一个个Delete了。

1.2 全局引用

全局引用可以在当前线程使用,也可以在其他线程使用,可以保存在本地的static静态变量或全局变量中,全局引用需要调用NewGlobalRel函数创建,释放时采用ReleaseGlobalRef函数释放。有效作用域在创建后,一直到调用ReleaseGlobalRef释放时。

1.3 弱全局引用

在Java1.2中,新增了弱全局引用,与全局变量一样其创建、删除均需要编程写出,也可以在本地多个代码中使用,也可以跨进程使用。不一样的是,它的存在不影响垃圾回收机制对该引用所指向对象实例的回收。其创建采用NewWeakGlobalRef,释放采用ReleaseWeakGlobalRel。

以上涉及的函数主要有以下几个:

//创建局部引用
jobject NewLocalRef(jobject obj);
//释放局部引用
void DeleteLocalRef(jobject obj);
//创建全局引用
jobject NewGlobalRef(jobject obj);
//释放全局引用
void DeleteGobalRef(jobject obj);
//创建弱全局引用
jobject NewWeakGlobalRef(jobject obj);
//释放弱全局引用
void DeleteWeakGlobalRef(jobject obj);
//该方法判断两个引用是否相等,对于弱全局引用如果对比的是NULL那么还可以判断该引用指向的对象是否被回收
jboolean IsSameObject(jobject obj1 , jobject obj2);

上述三中引用会影响内存的回收,在C/C++中没有向Java一样的垃圾回收机制,自己申请的内存要记得自己去释放了,否则会导致内存泄漏。虽然现在C/C++里面也有智能指针,但相对而言这个智能指针用起来不如Java。所以在C的世界里要遵循谁申请,谁释放的基本原则。

2. 举个栗子

上面介绍了基本知识,下面给出相应的例子来进行说明下。

2.1局部引用

    //1. 局部引用不要存储在static变量中,即使存了下次也不能用
    //static jclass cls;
    //以下创建的局部引用都放入到栈中
    env->PushLocalFrame(16);
    jclass cls;
    if (!cls) {//这里就错误了,前一次方法完成后jvm会释放局部引用,这里static存的值仅第一次有效
        cls = env->GetObjectClass(instance);//这里的cls是局部引用
    }

    //删除栈里面的局部引用
    env->PopLocalFrame(NULL);
    env->EnsureLocalCapacity(20);//将本地引用的最大限制改为20
    //下面可以进行其他操作。。。

在局部引用中要注意以下几方面:
1. 循环体内创建的局部引用,要在循环体内就直接释放了。
2. 编写的工具函数,里面创建的局部引用,要在该工具函数里面释放了。
3. 局部引用引用了一个大的Java对象,这时候一定一定要早点释放了。
4. 局部引用不要缓存在native层

2.2 全局引用

extern "C"
JNIEXPORT void JNICALL
Java_zqc_com_example_NativeTest_jniGlobalRef(JNIEnv *env, jobject instance) {
    static jobject obj;
    static jclass pCls;
    if (obj) {//第二次点击时,这里就不会空
        //由于obj和personCls被保存为全局引用了,所有这里使用仍然有效
        jmethodID getId = env->GetMethodID(pCls, "getName", "()Ljava/lang/String;");
        jstring name = (jstring) env->CallObjectMethod(obj, getId);
        LOGE("obj is not null, name:%s", jstringToChar(env, name));
        return;
    }
    if (!pCls) {//为空就去新建
        jclass tmpCls = env->FindClass("zqc/com/example/Person");
        pCls = (jclass) env->NewGlobalRef(tmpCls);
        env->DeleteLocalRef(tmpCls);
    }
    jmethodID conMid = env->GetMethodID(pCls, "<init>", "()V");
    jobject tmpObj = env->NewObject(pCls, conMid);
    jmethodID setId = env->GetMethodID(pCls, "setName", "(Ljava/lang/String;)V");
    env->CallVoidMethod(tmpObj, setId, env->NewStringUTF("看看姓名"));
    obj = env->NewGlobalRef(tmpObj);
    env->DeleteLocalRef(tmpObj);
}

2.3 弱全局引用

弱全局引用和全局引用基本差不多,最大的区别就是弱全局引用不影响GC的回收。在使用弱全局引用的时候一定要注意,使用前要检查下是不是被GC回收了。

extern "C"
JNIEXPORT void JNICALL
Java_zqc_com_example_NativeTest_jniWeakGlobalRef(JNIEnv *env, jobject instance) {
    static jclass pCls;
    if (!pCls) {
        jclass tmpCls = env->FindClass("zqc/com/example/Person");
        pCls = (jclass) env->NewWeakGlobalRef(tmpCls);
        env->DeleteLocalRef(tmpCls);
    }
    //除了第一次需要FindClass外,在没有回收pCls之前都可以使用

    //这里使用...

    //可以手动释放
    //env->DeleteWeakGlobalRef(pCls);
}

3. 引用的比较

jni提供了相应的函数

jboolean IsSameObject(jobject ref1, jobject ref2)
{ return functions->IsSameObject(this, ref1, ref2); }

如果两个引用指向同一个实例则返回JNI_TRUE,否则返回JNI_FALSE。

目录
相关文章
|
7天前
|
开发工具 Android开发
解决Android运行出现NDK at /Library/Android/sdk/ndk-bundle did not have a source.properties file
解决Android运行出现NDK at /Library/Android/sdk/ndk-bundle did not have a source.properties file
28 4
解决Android运行出现NDK at /Library/Android/sdk/ndk-bundle did not have a source.properties file
|
8天前
|
编解码 Android开发 iOS开发
探索安卓与iOS开发的差异:平台选择对项目成功的影响
在移动应用开发的世界中,安卓和iOS是两大主导力量。本文深入探讨了这两个平台在开发过程中的主要差异,并分析了这些差异如何影响项目的成功。通过对比分析,我们旨在为开发者提供决策时的参考,帮助他们根据项目需求和目标用户群体做出最合适的平台选择。
|
1天前
|
JavaScript 前端开发 Java
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
IT寒冬使APP开发门槛提升,安卓程序员需转型。选项包括:深化Android开发,跟进Google新技术如Kotlin、Jetpack、Flutter及Compose;研究Android底层框架,掌握AOSP;转型Java后端开发,学习Spring Boot等框架;拓展大前端技能,掌握JavaScript、Node.js、Vue.js及特定框架如微信小程序、HarmonyOS;或转向C/C++底层开发,通过音视频项目如FFmpeg积累经验。每条路径都有相应的书籍和技术栈推荐,助你顺利过渡。
9 3
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
|
5天前
|
Java Android开发 iOS开发
探索安卓与iOS开发的差异:平台选择对项目成功的影响
在移动应用开发的世界中,选择正确的平台是关键。本文通过比较安卓和iOS开发的核心差异,揭示平台选择如何影响应用的性能、用户体验和市场覆盖。我们将深入探讨各自的开发环境、编程语言、用户界面设计原则以及发布流程,以帮助开发者和企业做出明智的决策。
25 9
|
2天前
|
移动开发 开发工具 Android开发
探索安卓与iOS开发的差异:技术选择的影响
【8月更文挑战第17天】 在移动应用开发的广阔天地中,安卓和iOS两大平台各领风骚。本文通过比较这两个平台的编程语言、开发工具及市场策略,揭示了技术选择对开发者和产品成功的重要性。我们将从开发者的视角出发,深入探讨不同平台的技术特性及其对项目实施的具体影响,旨在为即将步入移动开发领域的新手提供一个清晰的指南,同时给予资深开发者新的思考角度。
|
5天前
|
Java 开发工具 Android开发
探索安卓与iOS开发的差异:从新手到专家的旅程
在数字时代的浪潮中,移动应用开发成为了连接世界的桥梁。本文将带你走进安卓与iOS这两大移动操作系统的开发世界,通过比较它们的编程语言、开发工具和环境、用户界面设计以及市场分布等方面,揭示各自的独特之处。无论你是初涉编程的新手,还是寻求进阶的开发者,这篇文章都将为你提供宝贵的洞见,助你在移动应用开发的征途上一帆风顺。
19 5
|
3天前
|
vr&ar Android开发 iOS开发
探索安卓和iOS开发的未来趋势
在移动应用开发的广阔天地里,安卓和iOS两大平台如同双子星座般璀璨夺目。随着技术的不断进步,这两个平台的开发趋势也在悄然发生着变化。本文将带你一探究竟,看看未来安卓和iOS开发将会迎来哪些令人激动的新特性和挑战。让我们一起跟随技术的脚步,开启这场探索之旅吧!
|
4天前
|
移动开发 Java Android开发
安卓与iOS开发:异同探析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文旨在深入探讨这两个平台在开发环境、编程语言、用户界面设计、性能优化及市场分布等方面的异同,为开发者提供实用的比较视角和决策参考。通过对比分析,我们不仅能更清晰地认识到各平台的特性,还能洞察未来移动开发的可能趋势。
|
5天前
|
Java 开发工具 Android开发
探索Android和iOS开发的差异与挑战
在移动应用开发的广阔天地中,Android和iOS两大平台如同两座高峰,各自拥有独特的风景。本文将深入探讨这两个平台的开发差异,包括编程语言、开发工具、用户界面设计等方面,并分析开发者面临的挑战。无论你是初涉移动应用开发的新手,还是已经在这条路上走了很远的老手,这篇文章都将为你提供新的视角和思考。让我们一起走进这个充满创新与挑战的世界,发现那些隐藏在代码背后的秘密。
|
6天前
|
编解码 Android开发 iOS开发
安卓与iOS开发:平台差异下的技术创新之路
在数字时代的浪潮中,移动应用开发如同两股潮流——安卓与iOS,各自携带着独特的技术生态和文化基因。本文将深入探讨这两大平台的开发环境、编程语言和工具的差异,以及它们如何塑造了不同的用户体验和技术趋势。通过比较分析,我们旨在揭示跨平台开发的可能性和挑战,同时探索未来技术创新的方向。让我们一起跟随代码的足迹,穿越安卓的开放草原和iOS的精密园林,发现那些隐藏在平台差异之下的创新机遇。
14 1